Show a patch.

GET /api/patches/20358/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 20358,
    "url": "https://patchwork.libcamera.org/api/patches/20358/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/20358/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20240620213607.32583-2-joelselvaraj.oss@gmail.com>",
    "date": "2024-06-20T21:36:06",
    "name": "[1/2] qcam: rotate the viewfinder output as per camera properties",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "a0301d4b55f50094f71876bc91560ba1dcf91c49",
    "submitter": {
        "id": 201,
        "url": "https://patchwork.libcamera.org/api/people/201/?format=api",
        "name": "Joel Selvaraj",
        "email": "joelselvaraj.oss@gmail.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/20358/mbox/",
    "series": [
        {
            "id": 4409,
            "url": "https://patchwork.libcamera.org/api/series/4409/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4409",
            "date": "2024-06-20T21:36:05",
            "name": "qcam: rotate the viewfinder output as per camera properties",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/4409/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/20358/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/20358/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>",
        "X-Original-To": "parsemail@patchwork.libcamera.org",
        "Delivered-To": "parsemail@patchwork.libcamera.org",
        "Received": [
            "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 1D4F2BD87C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 20 Jun 2024 21:46:16 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id CF330654A8;\n\tThu, 20 Jun 2024 23:46:13 +0200 (CEST)",
            "from mail-oi1-x22f.google.com (mail-oi1-x22f.google.com\n\t[IPv6:2607:f8b0:4864:20::22f])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2231C6548A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 20 Jun 2024 23:36:22 +0200 (CEST)",
            "by mail-oi1-x22f.google.com with SMTP id\n\t5614622812f47-3d1b5f32065so62764b6e.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 20 Jun 2024 14:36:22 -0700 (PDT)",
            "from localhost.localdomain ([67.6.32.220])\n\tby smtp.gmail.com with ESMTPSA id\n\t46e09a7af769-7009c65d116sm60530a34.57.2024.06.20.14.36.18\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tThu, 20 Jun 2024 14:36:19 -0700 (PDT)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"BbE1t8Ex\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=gmail.com; s=20230601; t=1718919380; x=1719524180;\n\tdarn=lists.libcamera.org; \n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:from:to:cc:subject:date\n\t:message-id:reply-to;\n\tbh=Hx3Yckl4F+rvU8xgCBznDKT1c44AyOLR3Mo9QHxDLVI=;\n\tb=BbE1t8Exppgw30f4bMjqTxjsAdiPnjauaQ6H5OYcKo8SP5M0zbNjZ3viQ0U9Xb5fB7\n\tDZnnyO98k/6GOvczagSnt/6QF6fJVYHnnP7pciXBU9poNHhLEqVxzCl3en7paBu34phR\n\tcGl5LmkPC8r50BTAYYwMpAJ/Xm/wG5lB+hSX0klagQWOuUTn8znEaHXtEdYTHZHxj3VR\n\tH2LKT5NejMQXeuqxXzH/Mn8jvs6OMr37SHwJAjwfu3MOs5Yxg9igvEzZhqkA4J4xwYub\n\tKL99Cpq+4F/0Oh4i/uLgcuIYzuy4QQviTAYtlvJKAwp2ggyGvY9VoQ9ERJUYYn1L//Zj\n\tBCLg==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1718919380; x=1719524180;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n\t:subject:date:message-id:reply-to;\n\tbh=Hx3Yckl4F+rvU8xgCBznDKT1c44AyOLR3Mo9QHxDLVI=;\n\tb=Vw6WdwH/dfSTH2RMqmB5SlSvwatYPE5aTNQ3IIF1Xln02zjTUUfk4SlKQiddHfuWbb\n\t89QasDGUF4mZiUqrd+gXydIc2lQ4F2PEpiugbA8RMNKyVJ48ny7no5qiR0slN3XoXrqF\n\tqysSRtg/2i+OMASVEmjh/bP1BhtgWE8yZrFPnsp8acGkxHckHMDnYilt5yNT21gAwZpA\n\t/paEXeq9hIxBqspmYNW/s7LKTjzeZ8c1iaObmVGOKuaoRZAR4IpKwWdq+ffDit05THAr\n\trENPRHkF1JnqPgQAWX1VVqoVOMFRRn6kfNXkR2t+dSbPmJdhCHcy4m0j9NAPOj3hmgC5\n\tdUuw==",
        "X-Gm-Message-State": "AOJu0YyLW+tLfjn4aw3HPeG3kAhEkCjXCKvOy7h5zoYxtoyVmOyCBJL3\n\t86an+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\n\t46e09a7af769-7007079cdf6mr6998438a34.0.1718919380031; \n\tThu, 20 Jun 2024 14:36:20 -0700 (PDT)",
        "From": "Joel Selvaraj <joelselvaraj.oss@gmail.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "Joel Selvaraj <joelselvaraj.oss@gmail.com>",
        "Subject": "[PATCH 1/2] qcam: rotate the viewfinder output as per camera\n\tproperties",
        "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",
        "Content-Transfer-Encoding": "8bit",
        "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": "<libcamera-devel.lists.libcamera.org>",
        "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>",
        "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>",
        "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>",
        "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "Devicetrees may specify the rotation at which the camera is\nmounted in the hardware. If available, this gets populated in camera\nproperties. Rotate the viewfinder output accordingly in qcam qt and\nopengl renderers.\n\nSigned-off-by: Joel Selvaraj <joelselvaraj.oss@gmail.com>\n---\n include/libcamera/orientation.h |  2 ++\n src/apps/qcam/main_window.cpp   |  9 ++++++-\n src/apps/qcam/viewfinder.h      |  4 ++-\n src/apps/qcam/viewfinder_gl.cpp | 46 ++++++++++++++++++++++++++++++---\n src/apps/qcam/viewfinder_gl.h   |  4 ++-\n src/apps/qcam/viewfinder_qt.cpp | 18 +++++++++++--\n src/apps/qcam/viewfinder_qt.h   |  5 +++-\n src/libcamera/orientation.cpp   | 34 ++++++++++++++++++++++++\n 8 files changed, 113 insertions(+), 9 deletions(-)",
    "diff": "diff --git a/include/libcamera/orientation.h b/include/libcamera/orientation.h\nindex a3b40e63..e32f5eb8 100644\n--- a/include/libcamera/orientation.h\n+++ b/include/libcamera/orientation.h\n@@ -25,6 +25,8 @@ enum class Orientation {\n \n Orientation orientationFromRotation(int angle, bool *success = nullptr);\n \n+int rotationFromOrientation(const Orientation &orientation, bool *success = nullptr);\n+\n std::ostream &operator<<(std::ostream &out, const Orientation &orientation);\n \n } /* namespace libcamera */\ndiff --git a/src/apps/qcam/main_window.cpp b/src/apps/qcam/main_window.cpp\nindex d515beed..18c94cf3 100644\n--- a/src/apps/qcam/main_window.cpp\n+++ b/src/apps/qcam/main_window.cpp\n@@ -363,6 +363,7 @@ void MainWindow::toggleCapture(bool start)\n int MainWindow::startCapture()\n {\n \tstd::vector<StreamRole> roles = StreamKeyValueParser::roles(options_[OptStream]);\n+\tOrientation orientation = Orientation::Rotate0;\n \tint ret;\n \n \t/* Verify roles are supported. */\n@@ -392,6 +393,12 @@ int MainWindow::startCapture()\n \t\treturn -EINVAL;\n \t}\n \n+\t/* Get orientation provided by camera kernel driver */\n+\tconst ControlList &properties = camera_->properties();\n+\tconst auto &rotation = properties.get(properties::Rotation);\n+\tif (rotation)\n+\t\torientation = orientationFromRotation(*rotation);\n+\n \tStreamConfiguration &vfConfig = config_->at(0);\n \n \t/* Use a format supported by the viewfinder if available. */\n@@ -444,7 +451,7 @@ int MainWindow::startCapture()\n \tret = viewfinder_->setFormat(vfConfig.pixelFormat,\n \t\t\t\t     QSize(vfConfig.size.width, vfConfig.size.height),\n \t\t\t\t     vfConfig.colorSpace.value_or(ColorSpace::Sycc),\n-\t\t\t\t     vfConfig.stride);\n+\t\t\t\t     vfConfig.stride, orientation);\n \tif (ret < 0) {\n \t\tqInfo() << \"Failed to set viewfinder format\";\n \t\treturn ret;\ndiff --git a/src/apps/qcam/viewfinder.h b/src/apps/qcam/viewfinder.h\nindex 914f88ec..f17cc4a1 100644\n--- a/src/apps/qcam/viewfinder.h\n+++ b/src/apps/qcam/viewfinder.h\n@@ -14,6 +14,7 @@\n #include <libcamera/color_space.h>\n #include <libcamera/formats.h>\n #include <libcamera/framebuffer.h>\n+#include <libcamera/orientation.h>\n \n class Image;\n \n@@ -26,7 +27,8 @@ public:\n \n \tvirtual int setFormat(const libcamera::PixelFormat &format, const QSize &size,\n \t\t\t      const libcamera::ColorSpace &colorSpace,\n-\t\t\t      unsigned int stride) = 0;\n+\t\t\t      unsigned int stride,\n+\t\t\t      const libcamera::Orientation &orientation) = 0;\n \tvirtual void render(libcamera::FrameBuffer *buffer, Image *image) = 0;\n \tvirtual void stop() = 0;\n \ndiff --git a/src/apps/qcam/viewfinder_gl.cpp b/src/apps/qcam/viewfinder_gl.cpp\nindex 9d2a6960..c4b10e1e 100644\n--- a/src/apps/qcam/viewfinder_gl.cpp\n+++ b/src/apps/qcam/viewfinder_gl.cpp\n@@ -77,7 +77,7 @@ const QList<libcamera::PixelFormat> &ViewFinderGL::nativeFormats() const\n \n int ViewFinderGL::setFormat(const libcamera::PixelFormat &format, const QSize &size,\n \t\t\t    const libcamera::ColorSpace &colorSpace,\n-\t\t\t    unsigned int stride)\n+\t\t\t    unsigned int stride, const libcamera::Orientation &orientation)\n {\n \tif (format != format_ || colorSpace != colorSpace_) {\n \t\t/*\n@@ -101,6 +101,7 @@ int ViewFinderGL::setFormat(const libcamera::PixelFormat &format, const QSize &s\n \n \tsize_ = size;\n \tstride_ = stride;\n+\torientation_ = orientation;\n \n \tupdateGeometry();\n \treturn 0;\n@@ -511,7 +512,7 @@ void ViewFinderGL::initializeGL()\n \tglEnable(GL_TEXTURE_2D);\n \tglDisable(GL_DEPTH_TEST);\n \n-\tstatic const GLfloat coordinates[2][4][2]{\n+\tGLfloat coordinates[2][4][2]{\n \t\t{\n \t\t\t/* Vertex coordinates */\n \t\t\t{ -1.0f, -1.0f },\n@@ -528,6 +529,41 @@ void ViewFinderGL::initializeGL()\n \t\t},\n \t};\n \n+\tswitch (orientation_) {\n+\tcase libcamera::Orientation::Rotate90:\n+\t\tcoordinates[0][0][0] = -1.0f;\n+\t\tcoordinates[0][0][1] = +1.0f;\n+\t\tcoordinates[0][1][0] = +1.0f;\n+\t\tcoordinates[0][1][1] = +1.0f;\n+\t\tcoordinates[0][2][0] = +1.0f;\n+\t\tcoordinates[0][2][1] = -1.0f;\n+\t\tcoordinates[0][3][0] = -1.0f;\n+\t\tcoordinates[0][3][1] = -1.0f;\n+\t\tbreak;\n+\tcase libcamera::Orientation::Rotate180:\n+\t\tcoordinates[0][0][0] = +1.0f;\n+\t\tcoordinates[0][0][1] = +1.0f;\n+\t\tcoordinates[0][1][0] = +1.0f;\n+\t\tcoordinates[0][1][1] = -1.0f;\n+\t\tcoordinates[0][2][0] = -1.0f;\n+\t\tcoordinates[0][2][1] = -1.0f;\n+\t\tcoordinates[0][3][0] = -1.0f;\n+\t\tcoordinates[0][3][1] = +1.0f;\n+\t\tbreak;\n+\tcase libcamera::Orientation::Rotate270:\n+\t\tcoordinates[0][0][0] = +1.0f;\n+\t\tcoordinates[0][0][1] = -1.0f;\n+\t\tcoordinates[0][1][0] = -1.0f;\n+\t\tcoordinates[0][1][1] = -1.0f;\n+\t\tcoordinates[0][2][0] = -1.0f;\n+\t\tcoordinates[0][2][1] = +1.0f;\n+\t\tcoordinates[0][3][0] = +1.0f;\n+\t\tcoordinates[0][3][1] = +1.0f;\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n \tvertexBuffer_.create();\n \tvertexBuffer_.bind();\n \tvertexBuffer_.allocate(coordinates, sizeof(coordinates));\n@@ -831,5 +867,9 @@ void ViewFinderGL::resizeGL(int w, int h)\n \n QSize ViewFinderGL::sizeHint() const\n {\n-\treturn size_.isValid() ? size_ : QSize(640, 480);\n+\tif (orientation_ == libcamera::Orientation::Rotate90 ||\n+\t    orientation_ == libcamera::Orientation::Rotate270)\n+\t\treturn size_.isValid() ? size_.transposed() : QSize(480, 640);\n+\telse\n+\t\treturn size_.isValid() ? size_ : QSize(640, 480);\n }\ndiff --git a/src/apps/qcam/viewfinder_gl.h b/src/apps/qcam/viewfinder_gl.h\nindex 23744b41..1fc0f827 100644\n--- a/src/apps/qcam/viewfinder_gl.h\n+++ b/src/apps/qcam/viewfinder_gl.h\n@@ -39,7 +39,8 @@ public:\n \n \tint setFormat(const libcamera::PixelFormat &format, const QSize &size,\n \t\t      const libcamera::ColorSpace &colorSpace,\n-\t\t      unsigned int stride) override;\n+\t\t      unsigned int stride,\n+\t\t      const libcamera::Orientation &orientation) override;\n \tvoid render(libcamera::FrameBuffer *buffer, Image *image) override;\n \tvoid stop() override;\n \n@@ -68,6 +69,7 @@ private:\n \tlibcamera::FrameBuffer *buffer_;\n \tlibcamera::PixelFormat format_;\n \tlibcamera::ColorSpace colorSpace_;\n+\tlibcamera::Orientation orientation_;\n \tQSize size_;\n \tunsigned int stride_;\n \tImage *image_;\ndiff --git a/src/apps/qcam/viewfinder_qt.cpp b/src/apps/qcam/viewfinder_qt.cpp\nindex 4821c27d..583a74f7 100644\n--- a/src/apps/qcam/viewfinder_qt.cpp\n+++ b/src/apps/qcam/viewfinder_qt.cpp\n@@ -57,7 +57,7 @@ const QList<libcamera::PixelFormat> &ViewFinderQt::nativeFormats() const\n \n int ViewFinderQt::setFormat(const libcamera::PixelFormat &format, const QSize &size,\n \t\t\t    [[maybe_unused]] const libcamera::ColorSpace &colorSpace,\n-\t\t\t    unsigned int stride)\n+\t\t\t    unsigned int stride, const libcamera::Orientation &orientation)\n {\n \timage_ = QImage();\n \n@@ -80,6 +80,15 @@ int ViewFinderQt::setFormat(const libcamera::PixelFormat &format, const QSize &s\n \n \tformat_ = format;\n \tsize_ = size;\n+\torientation_ = orientation;\n+\n+\tbool success;\n+\tint angle = libcamera::rotationFromOrientation(orientation, &success);\n+\tif (!success) {\n+\t\tqWarning() << \"Unsupported orientation\";\n+\t\treturn -EINVAL;\n+\t}\n+\ttransform_ = QTransform().rotate(angle);\n \n \tupdateGeometry();\n \treturn 0;\n@@ -148,6 +157,7 @@ void ViewFinderQt::paintEvent(QPaintEvent *)\n \n \t/* If we have an image, draw it. */\n \tif (!image_.isNull()) {\n+\t\timage_ = image_.transformed(transform_);\n \t\tpainter.drawImage(rect(), image_, image_.rect());\n \t\treturn;\n \t}\n@@ -179,5 +189,9 @@ void ViewFinderQt::paintEvent(QPaintEvent *)\n \n QSize ViewFinderQt::sizeHint() const\n {\n-\treturn size_.isValid() ? size_ : QSize(640, 480);\n+\tif (orientation_ == libcamera::Orientation::Rotate90 ||\n+\t    orientation_ == libcamera::Orientation::Rotate270)\n+\t\treturn size_.isValid() ? size_.transposed() : QSize(480, 640);\n+\telse\n+\t\treturn size_.isValid() ? size_ : QSize(640, 480);\n }\ndiff --git a/src/apps/qcam/viewfinder_qt.h b/src/apps/qcam/viewfinder_qt.h\nindex 4f4b9f11..309b39e5 100644\n--- a/src/apps/qcam/viewfinder_qt.h\n+++ b/src/apps/qcam/viewfinder_qt.h\n@@ -33,7 +33,8 @@ public:\n \n \tint setFormat(const libcamera::PixelFormat &format, const QSize &size,\n \t\t      const libcamera::ColorSpace &colorSpace,\n-\t\t      unsigned int stride) override;\n+\t\t      unsigned int stride,\n+\t\t      const libcamera::Orientation &orientation) override;\n \tvoid render(libcamera::FrameBuffer *buffer, Image *image) override;\n \tvoid stop() override;\n \n@@ -51,6 +52,8 @@ private:\n \n \tlibcamera::PixelFormat format_;\n \tQSize size_;\n+\tQTransform transform_;\n+\tlibcamera::Orientation orientation_;\n \n \t/* Camera stopped icon */\n \tQSize vfSize_;\ndiff --git a/src/libcamera/orientation.cpp b/src/libcamera/orientation.cpp\nindex 47fd6a32..7afb37a8 100644\n--- a/src/libcamera/orientation.cpp\n+++ b/src/libcamera/orientation.cpp\n@@ -92,6 +92,40 @@ Orientation orientationFromRotation(int angle, bool *success)\n \treturn Orientation::Rotate0;\n }\n \n+/**\n+ * \\brief Return the rotation angle for a given orientation\n+ * \\param[in] orientation The orientation to convert to a rotation angle\n+ * \\param[out] success Set to `true` if the given orientation is valid,\n+ * otherwise `false`\n+ * \\return The rotation angle corresponding to the given orientation\n+ * if \\a success was set to `true`, otherwise 0.\n+ */\n+int rotationFromOrientation(const Orientation &orientation, bool *success)\n+{\n+\tif (success != nullptr)\n+\t\t*success = true;\n+\n+\tswitch (orientation) {\n+\tcase Orientation::Rotate0:\n+\tcase Orientation::Rotate0Mirror:\n+\t\treturn 0;\n+\tcase Orientation::Rotate90:\n+\tcase Orientation::Rotate90Mirror:\n+\t\treturn 90;\n+\tcase Orientation::Rotate180:\n+\tcase Orientation::Rotate180Mirror:\n+\t\treturn 180;\n+\tcase Orientation::Rotate270:\n+\tcase Orientation::Rotate270Mirror:\n+\t\treturn 270;\n+\t}\n+\n+\tif (success != nullptr)\n+\t\t*success = false;\n+\n+\treturn 0;\n+}\n+\n /**\n  * \\brief Prints human-friendly names for Orientation items\n  * \\param[in] out The output stream\n",
    "prefixes": [
        "1/2"
    ]
}