From patchwork Thu Jun 20 21:36:06 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joel Selvaraj X-Patchwork-Id: 20358 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 1D4F2BD87C for ; Thu, 20 Jun 2024 21:46:16 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id CF330654A8; Thu, 20 Jun 2024 23:46:13 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="BbE1t8Ex"; dkim-atps=neutral Received: from mail-oi1-x22f.google.com (mail-oi1-x22f.google.com [IPv6:2607:f8b0:4864:20::22f]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2231C6548A for ; Thu, 20 Jun 2024 23:36:22 +0200 (CEST) Received: by mail-oi1-x22f.google.com with SMTP id 5614622812f47-3d1b5f32065so62764b6e.2 for ; Thu, 20 Jun 2024 14:36:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1718919380; x=1719524180; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Hx3Yckl4F+rvU8xgCBznDKT1c44AyOLR3Mo9QHxDLVI=; b=BbE1t8Exppgw30f4bMjqTxjsAdiPnjauaQ6H5OYcKo8SP5M0zbNjZ3viQ0U9Xb5fB7 DZnnyO98k/6GOvczagSnt/6QF6fJVYHnnP7pciXBU9poNHhLEqVxzCl3en7paBu34phR cGl5LmkPC8r50BTAYYwMpAJ/Xm/wG5lB+hSX0klagQWOuUTn8znEaHXtEdYTHZHxj3VR H2LKT5NejMQXeuqxXzH/Mn8jvs6OMr37SHwJAjwfu3MOs5Yxg9igvEzZhqkA4J4xwYub KL99Cpq+4F/0Oh4i/uLgcuIYzuy4QQviTAYtlvJKAwp2ggyGvY9VoQ9ERJUYYn1L//Zj BCLg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718919380; x=1719524180; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Hx3Yckl4F+rvU8xgCBznDKT1c44AyOLR3Mo9QHxDLVI=; b=Vw6WdwH/dfSTH2RMqmB5SlSvwatYPE5aTNQ3IIF1Xln02zjTUUfk4SlKQiddHfuWbb 89QasDGUF4mZiUqrd+gXydIc2lQ4F2PEpiugbA8RMNKyVJ48ny7no5qiR0slN3XoXrqF qysSRtg/2i+OMASVEmjh/bP1BhtgWE8yZrFPnsp8acGkxHckHMDnYilt5yNT21gAwZpA /paEXeq9hIxBqspmYNW/s7LKTjzeZ8c1iaObmVGOKuaoRZAR4IpKwWdq+ffDit05THAr rENPRHkF1JnqPgQAWX1VVqoVOMFRRn6kfNXkR2t+dSbPmJdhCHcy4m0j9NAPOj3hmgC5 dUuw== X-Gm-Message-State: AOJu0YyLW+tLfjn4aw3HPeG3kAhEkCjXCKvOy7h5zoYxtoyVmOyCBJL3 86an+vf9AN9kPXht7IV1iin8Bqc8vikiDHdiRuuEklcQz+zC/yAwg/Dxzjx4 X-Google-Smtp-Source: AGHT+IFtjWGzGXqSKa/kFK3hmpFX8lMcw0FkoclZr0NPpLnDoxeNUHkUFnfkocPQhhIWMqqoHUNtjg== X-Received: by 2002:a05:6830:3207:b0:6fa:106b:8d9b with SMTP id 46e09a7af769-7007079cdf6mr6998438a34.0.1718919380031; Thu, 20 Jun 2024 14:36:20 -0700 (PDT) Received: from localhost.localdomain ([67.6.32.220]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7009c65d116sm60530a34.57.2024.06.20.14.36.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 20 Jun 2024 14:36:19 -0700 (PDT) From: Joel Selvaraj To: libcamera-devel@lists.libcamera.org Cc: Joel Selvaraj Subject: [PATCH 1/2] qcam: rotate the viewfinder output as per camera properties Date: Thu, 20 Jun 2024 16:36:06 -0500 Message-ID: <20240620213607.32583-2-joelselvaraj.oss@gmail.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240620213607.32583-1-joelselvaraj.oss@gmail.com> References: <20240620213607.32583-1-joelselvaraj.oss@gmail.com> MIME-Version: 1.0 X-Mailman-Approved-At: Thu, 20 Jun 2024 23:46:10 +0200 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Devicetrees may specify the rotation at which the camera is mounted in the hardware. If available, this gets populated in camera properties. Rotate the viewfinder output accordingly in qcam qt and opengl renderers. Signed-off-by: Joel Selvaraj --- include/libcamera/orientation.h | 2 ++ src/apps/qcam/main_window.cpp | 9 ++++++- src/apps/qcam/viewfinder.h | 4 ++- src/apps/qcam/viewfinder_gl.cpp | 46 ++++++++++++++++++++++++++++++--- src/apps/qcam/viewfinder_gl.h | 4 ++- src/apps/qcam/viewfinder_qt.cpp | 18 +++++++++++-- src/apps/qcam/viewfinder_qt.h | 5 +++- src/libcamera/orientation.cpp | 34 ++++++++++++++++++++++++ 8 files changed, 113 insertions(+), 9 deletions(-) diff --git a/include/libcamera/orientation.h b/include/libcamera/orientation.h index a3b40e63..e32f5eb8 100644 --- a/include/libcamera/orientation.h +++ b/include/libcamera/orientation.h @@ -25,6 +25,8 @@ enum class Orientation { Orientation orientationFromRotation(int angle, bool *success = nullptr); +int rotationFromOrientation(const Orientation &orientation, bool *success = nullptr); + std::ostream &operator<<(std::ostream &out, const Orientation &orientation); } /* namespace libcamera */ diff --git a/src/apps/qcam/main_window.cpp b/src/apps/qcam/main_window.cpp index d515beed..18c94cf3 100644 --- a/src/apps/qcam/main_window.cpp +++ b/src/apps/qcam/main_window.cpp @@ -363,6 +363,7 @@ void MainWindow::toggleCapture(bool start) int MainWindow::startCapture() { std::vector roles = StreamKeyValueParser::roles(options_[OptStream]); + Orientation orientation = Orientation::Rotate0; int ret; /* Verify roles are supported. */ @@ -392,6 +393,12 @@ int MainWindow::startCapture() return -EINVAL; } + /* Get orientation provided by camera kernel driver */ + const ControlList &properties = camera_->properties(); + const auto &rotation = properties.get(properties::Rotation); + if (rotation) + orientation = orientationFromRotation(*rotation); + StreamConfiguration &vfConfig = config_->at(0); /* Use a format supported by the viewfinder if available. */ @@ -444,7 +451,7 @@ int MainWindow::startCapture() ret = viewfinder_->setFormat(vfConfig.pixelFormat, QSize(vfConfig.size.width, vfConfig.size.height), vfConfig.colorSpace.value_or(ColorSpace::Sycc), - vfConfig.stride); + vfConfig.stride, orientation); if (ret < 0) { qInfo() << "Failed to set viewfinder format"; return ret; diff --git a/src/apps/qcam/viewfinder.h b/src/apps/qcam/viewfinder.h index 914f88ec..f17cc4a1 100644 --- a/src/apps/qcam/viewfinder.h +++ b/src/apps/qcam/viewfinder.h @@ -14,6 +14,7 @@ #include #include #include +#include class Image; @@ -26,7 +27,8 @@ public: virtual int setFormat(const libcamera::PixelFormat &format, const QSize &size, const libcamera::ColorSpace &colorSpace, - unsigned int stride) = 0; + unsigned int stride, + const libcamera::Orientation &orientation) = 0; virtual void render(libcamera::FrameBuffer *buffer, Image *image) = 0; virtual void stop() = 0; diff --git a/src/apps/qcam/viewfinder_gl.cpp b/src/apps/qcam/viewfinder_gl.cpp index 9d2a6960..c4b10e1e 100644 --- a/src/apps/qcam/viewfinder_gl.cpp +++ b/src/apps/qcam/viewfinder_gl.cpp @@ -77,7 +77,7 @@ const QList &ViewFinderGL::nativeFormats() const int ViewFinderGL::setFormat(const libcamera::PixelFormat &format, const QSize &size, const libcamera::ColorSpace &colorSpace, - unsigned int stride) + unsigned int stride, const libcamera::Orientation &orientation) { if (format != format_ || colorSpace != colorSpace_) { /* @@ -101,6 +101,7 @@ int ViewFinderGL::setFormat(const libcamera::PixelFormat &format, const QSize &s size_ = size; stride_ = stride; + orientation_ = orientation; updateGeometry(); return 0; @@ -511,7 +512,7 @@ void ViewFinderGL::initializeGL() glEnable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); - static const GLfloat coordinates[2][4][2]{ + GLfloat coordinates[2][4][2]{ { /* Vertex coordinates */ { -1.0f, -1.0f }, @@ -528,6 +529,41 @@ void ViewFinderGL::initializeGL() }, }; + switch (orientation_) { + case libcamera::Orientation::Rotate90: + coordinates[0][0][0] = -1.0f; + coordinates[0][0][1] = +1.0f; + coordinates[0][1][0] = +1.0f; + coordinates[0][1][1] = +1.0f; + coordinates[0][2][0] = +1.0f; + coordinates[0][2][1] = -1.0f; + coordinates[0][3][0] = -1.0f; + coordinates[0][3][1] = -1.0f; + break; + case libcamera::Orientation::Rotate180: + coordinates[0][0][0] = +1.0f; + coordinates[0][0][1] = +1.0f; + coordinates[0][1][0] = +1.0f; + coordinates[0][1][1] = -1.0f; + coordinates[0][2][0] = -1.0f; + coordinates[0][2][1] = -1.0f; + coordinates[0][3][0] = -1.0f; + coordinates[0][3][1] = +1.0f; + break; + case libcamera::Orientation::Rotate270: + coordinates[0][0][0] = +1.0f; + coordinates[0][0][1] = -1.0f; + coordinates[0][1][0] = -1.0f; + coordinates[0][1][1] = -1.0f; + coordinates[0][2][0] = -1.0f; + coordinates[0][2][1] = +1.0f; + coordinates[0][3][0] = +1.0f; + coordinates[0][3][1] = +1.0f; + break; + default: + break; + } + vertexBuffer_.create(); vertexBuffer_.bind(); vertexBuffer_.allocate(coordinates, sizeof(coordinates)); @@ -831,5 +867,9 @@ void ViewFinderGL::resizeGL(int w, int h) QSize ViewFinderGL::sizeHint() const { - return size_.isValid() ? size_ : QSize(640, 480); + if (orientation_ == libcamera::Orientation::Rotate90 || + orientation_ == libcamera::Orientation::Rotate270) + return size_.isValid() ? size_.transposed() : QSize(480, 640); + else + return size_.isValid() ? size_ : QSize(640, 480); } diff --git a/src/apps/qcam/viewfinder_gl.h b/src/apps/qcam/viewfinder_gl.h index 23744b41..1fc0f827 100644 --- a/src/apps/qcam/viewfinder_gl.h +++ b/src/apps/qcam/viewfinder_gl.h @@ -39,7 +39,8 @@ public: int setFormat(const libcamera::PixelFormat &format, const QSize &size, const libcamera::ColorSpace &colorSpace, - unsigned int stride) override; + unsigned int stride, + const libcamera::Orientation &orientation) override; void render(libcamera::FrameBuffer *buffer, Image *image) override; void stop() override; @@ -68,6 +69,7 @@ private: libcamera::FrameBuffer *buffer_; libcamera::PixelFormat format_; libcamera::ColorSpace colorSpace_; + libcamera::Orientation orientation_; QSize size_; unsigned int stride_; Image *image_; diff --git a/src/apps/qcam/viewfinder_qt.cpp b/src/apps/qcam/viewfinder_qt.cpp index 4821c27d..583a74f7 100644 --- a/src/apps/qcam/viewfinder_qt.cpp +++ b/src/apps/qcam/viewfinder_qt.cpp @@ -57,7 +57,7 @@ const QList &ViewFinderQt::nativeFormats() const int ViewFinderQt::setFormat(const libcamera::PixelFormat &format, const QSize &size, [[maybe_unused]] const libcamera::ColorSpace &colorSpace, - unsigned int stride) + unsigned int stride, const libcamera::Orientation &orientation) { image_ = QImage(); @@ -80,6 +80,15 @@ int ViewFinderQt::setFormat(const libcamera::PixelFormat &format, const QSize &s format_ = format; size_ = size; + orientation_ = orientation; + + bool success; + int angle = libcamera::rotationFromOrientation(orientation, &success); + if (!success) { + qWarning() << "Unsupported orientation"; + return -EINVAL; + } + transform_ = QTransform().rotate(angle); updateGeometry(); return 0; @@ -148,6 +157,7 @@ void ViewFinderQt::paintEvent(QPaintEvent *) /* If we have an image, draw it. */ if (!image_.isNull()) { + image_ = image_.transformed(transform_); painter.drawImage(rect(), image_, image_.rect()); return; } @@ -179,5 +189,9 @@ void ViewFinderQt::paintEvent(QPaintEvent *) QSize ViewFinderQt::sizeHint() const { - return size_.isValid() ? size_ : QSize(640, 480); + if (orientation_ == libcamera::Orientation::Rotate90 || + orientation_ == libcamera::Orientation::Rotate270) + return size_.isValid() ? size_.transposed() : QSize(480, 640); + else + return size_.isValid() ? size_ : QSize(640, 480); } diff --git a/src/apps/qcam/viewfinder_qt.h b/src/apps/qcam/viewfinder_qt.h index 4f4b9f11..309b39e5 100644 --- a/src/apps/qcam/viewfinder_qt.h +++ b/src/apps/qcam/viewfinder_qt.h @@ -33,7 +33,8 @@ public: int setFormat(const libcamera::PixelFormat &format, const QSize &size, const libcamera::ColorSpace &colorSpace, - unsigned int stride) override; + unsigned int stride, + const libcamera::Orientation &orientation) override; void render(libcamera::FrameBuffer *buffer, Image *image) override; void stop() override; @@ -51,6 +52,8 @@ private: libcamera::PixelFormat format_; QSize size_; + QTransform transform_; + libcamera::Orientation orientation_; /* Camera stopped icon */ QSize vfSize_; diff --git a/src/libcamera/orientation.cpp b/src/libcamera/orientation.cpp index 47fd6a32..7afb37a8 100644 --- a/src/libcamera/orientation.cpp +++ b/src/libcamera/orientation.cpp @@ -92,6 +92,40 @@ Orientation orientationFromRotation(int angle, bool *success) return Orientation::Rotate0; } +/** + * \brief Return the rotation angle for a given orientation + * \param[in] orientation The orientation to convert to a rotation angle + * \param[out] success Set to `true` if the given orientation is valid, + * otherwise `false` + * \return The rotation angle corresponding to the given orientation + * if \a success was set to `true`, otherwise 0. + */ +int rotationFromOrientation(const Orientation &orientation, bool *success) +{ + if (success != nullptr) + *success = true; + + switch (orientation) { + case Orientation::Rotate0: + case Orientation::Rotate0Mirror: + return 0; + case Orientation::Rotate90: + case Orientation::Rotate90Mirror: + return 90; + case Orientation::Rotate180: + case Orientation::Rotate180Mirror: + return 180; + case Orientation::Rotate270: + case Orientation::Rotate270Mirror: + return 270; + } + + if (success != nullptr) + *success = false; + + return 0; +} + /** * \brief Prints human-friendly names for Orientation items * \param[in] out The output stream