Show a patch.

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

{
    "id": 24526,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/24526/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/24526/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/1.1/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": "<20250930122726.1837524-28-stefan.klug@ideasonboard.com>",
    "date": "2025-09-30T12:26:48",
    "name": "[v1,27/33] libcamera: rkisp1: Handle requested orientation using dewarper",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "edef067532e81c684219b63349750b8671230cda",
    "submitter": {
        "id": 184,
        "url": "https://patchwork.libcamera.org/api/1.1/people/184/?format=api",
        "name": "Stefan Klug",
        "email": "stefan.klug@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/24526/mbox/",
    "series": [
        {
            "id": 5468,
            "url": "https://patchwork.libcamera.org/api/1.1/series/5468/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5468",
            "date": "2025-09-30T12:26:21",
            "name": "Full dewarper support on imx8mp",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/5468/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/24526/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/24526/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 87C47C328C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 30 Sep 2025 13:31:38 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 484156B599;\n\tTue, 30 Sep 2025 15:31:37 +0200 (CEST)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3308D62C35\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 30 Sep 2025 15:31:35 +0200 (CEST)",
            "from ideasonboard.com (unknown [94.31.94.171])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id E0274228;\n\tTue, 30 Sep 2025 15:30:06 +0200 (CEST)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"ej7hp4N4\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1759239007;\n\tbh=nqlJJh22d5rsGn1yvoRpZ2weJ1i2js9fjhlM/L3T2pQ=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=ej7hp4N4CiPsJyuwIzBWa4mET65zZ7NyIk+CgxqLpKX2CH3brqkridFDpMcPfdsoT\n\t9woKM5eaTbt+EJzWlZxH4lM6fb7VnG7sdtC3COhcRw0d58ATmXXShOcM449D0kEFsi\n\tCBWhKQOwMQEsWkfdxYntGu/F146LPDNi4uREFy4o=",
        "From": "Stefan Klug <stefan.klug@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "Stefan Klug <stefan.klug@ideasonboard.com>",
        "Subject": "[PATCH v1 27/33] libcamera: rkisp1: Handle requested orientation\n\tusing dewarper",
        "Date": "Tue, 30 Sep 2025 14:26:48 +0200",
        "Message-ID": "<20250930122726.1837524-28-stefan.klug@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.48.1",
        "In-Reply-To": "<20250930122726.1837524-1-stefan.klug@ideasonboard.com>",
        "References": "<20250930122726.1837524-1-stefan.klug@ideasonboard.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "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": "When the dewarper is present it can handle arbitrary orientations\nspecified in the requested camera configuration. In that case handle all\ntransformations inside the dewarper (even if the sensor supports some of\nthem) because that makes it easier to handle coordinates for lens\ndewarping inside the dewarper.\n\nSigned-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n---\n src/libcamera/pipeline/rkisp1/rkisp1.cpp | 79 +++++++++++++++++++-----\n 1 file changed, 63 insertions(+), 16 deletions(-)",
    "diff": "diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\nindex e8902ce66b70..1046930857e1 100644\n--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n@@ -550,11 +550,6 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate()\n \t\tstatus = Adjusted;\n \t}\n \n-\tOrientation requestedOrientation = orientation;\n-\tcombinedTransform_ = data_->sensor_->computeTransform(&orientation);\n-\tif (orientation != requestedOrientation)\n-\t\tstatus = Adjusted;\n-\n \t/*\n \t * Simultaneous capture of raw and processed streams isn't possible. If\n \t * there is any raw stream, cap the number of streams to one.\n@@ -570,6 +565,12 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate()\n \t\t}\n \t}\n \n+\t/*\n+\t * If the dewarper supports orientation adjustments, apply that completely\n+\t * there. Even if the sensor supports flips, it is beneficial to do that\n+\t * in the dewarper so that lens dewarping happens on the unflipped image\n+\t */\n+\tbool transposeAfterIsp = false;\n \tbool useDewarper = false;\n \tif (pipe->dewarper_) {\n \t\t/*\n@@ -580,6 +581,14 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate()\n \t\t\t     PixelFormatInfo::ColourEncodingRAW;\n \t\tif (!isRaw)\n \t\t\tuseDewarper = true;\n+\t\tcombinedTransform_ = orientation / data_->sensor_->mountingOrientation();\n+\t\tif (!!(combinedTransform_ & Transform::Transpose))\n+\t\t\ttransposeAfterIsp = true;\n+\t} else {\n+\t\tOrientation requestedOrientation = orientation;\n+\t\tcombinedTransform_ = data_->sensor_->computeTransform(&orientation);\n+\t\tif (orientation != requestedOrientation)\n+\t\t\tstatus = Adjusted;\n \t}\n \n \t/*\n@@ -610,6 +619,9 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate()\n \t\t    (expectedStatus == Valid && ret == Adjusted))\n \t\t\treturn false;\n \n+\t\tif (transposeAfterIsp)\n+\t\t\ttryCfg.size.transpose();\n+\n \t\tif (useDewarper) {\n \t\t\t/*\n \t\t\t * The dewarper output is independent of the ISP path.\n@@ -694,6 +706,9 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate()\n \t\tmaxSize = std::max(maxSize, cfg.size);\n \t}\n \n+\tif (transposeAfterIsp)\n+\t\tmaxSize.transpose();\n+\n \tstd::vector<unsigned int> mbusCodes;\n \n \tif (rawFormat.isValid()) {\n@@ -739,6 +754,22 @@ PipelineHandlerRkISP1::generateConfiguration(Camera *camera,\n \tif (roles.empty())\n \t\treturn config;\n \n+\tTransform transform = Transform::Identity;\n+\tSize previewSize = kRkISP1PreviewSize;\n+\tbool transposeAfterIsp = false;\n+\tif (data->canUseDewarper_) {\n+\t\ttransform = Orientation::Rotate0 / data->sensor_->mountingOrientation();\n+\t\tif (!!(transform & Transform::Transpose))\n+\t\t\ttransposeAfterIsp = true;\n+\t}\n+\n+\t/*\n+\t * In case of a transpose transform we need to create a path for the\n+\t * transposed size.\n+\t */\n+\tif (transposeAfterIsp)\n+\t\tpreviewSize.transpose();\n+\n \t/*\n \t * As the ISP can't output different color spaces for the main and self\n \t * path, pick a sensible default color space based on the role of the\n@@ -767,7 +798,7 @@ PipelineHandlerRkISP1::generateConfiguration(Camera *camera,\n \t\t\tif (!colorSpace)\n \t\t\t\tcolorSpace = ColorSpace::Sycc;\n \n-\t\t\tsize = kRkISP1PreviewSize;\n+\t\t\tsize = previewSize;\n \t\t\tbreak;\n \n \t\tcase StreamRole::VideoRecording:\n@@ -775,7 +806,7 @@ PipelineHandlerRkISP1::generateConfiguration(Camera *camera,\n \t\t\tif (!colorSpace)\n \t\t\t\tcolorSpace = ColorSpace::Rec709;\n \n-\t\t\tsize = kRkISP1PreviewSize;\n+\t\t\tsize = previewSize;\n \t\t\tbreak;\n \n \t\tcase StreamRole::Raw:\n@@ -817,6 +848,9 @@ PipelineHandlerRkISP1::generateConfiguration(Camera *camera,\n \t\tif (!cfg.pixelFormat.isValid())\n \t\t\treturn nullptr;\n \n+\t\tif (transposeAfterIsp && role != StreamRole::Raw)\n+\t\t\tcfg.size.transpose();\n+\n \t\tcfg.colorSpace = colorSpace;\n \t\tcfg.bufferCount = kRkISP1MinBufferCount;\n \t\tconfig->addConfiguration(cfg);\n@@ -839,6 +873,19 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)\n \tif (ret)\n \t\treturn ret;\n \n+\tconst PixelFormat &streamFormat = config->at(0).pixelFormat;\n+\tconst PixelFormatInfo &info = PixelFormatInfo::info(streamFormat);\n+\tisRaw_ = info.colourEncoding == PixelFormatInfo::ColourEncodingRAW;\n+\tdata->usesDewarper_ = data->canUseDewarper_ && !isRaw_;\n+\n+\tTransform transform = config->combinedTransform();\n+\tbool transposeAfterIsp = false;\n+\tif (data->usesDewarper_) {\n+\t\tif (!!(transform & Transform::Transpose))\n+\t\t\ttransposeAfterIsp = true;\n+\t\ttransform = Transform::Identity;\n+\t}\n+\n \t/*\n \t * Configure the format on the sensor output and propagate it through\n \t * the pipeline.\n@@ -848,10 +895,10 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)\n \n \tif (config->sensorConfig)\n \t\tret = sensor->applyConfiguration(*config->sensorConfig,\n-\t\t\t\t\t\t config->combinedTransform(),\n+\t\t\t\t\t\t transform,\n \t\t\t\t\t\t &format);\n \telse\n-\t\tret = sensor->setFormat(&format, config->combinedTransform());\n+\t\tret = sensor->setFormat(&format, transform);\n \n \tif (ret < 0)\n \t\treturn ret;\n@@ -878,10 +925,6 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)\n \t\t<< \" crop \" << inputCrop;\n \n \tRectangle outputCrop = inputCrop;\n-\tconst PixelFormat &streamFormat = config->at(0).pixelFormat;\n-\tconst PixelFormatInfo &info = PixelFormatInfo::info(streamFormat);\n-\tisRaw_ = info.colourEncoding == PixelFormatInfo::ColourEncodingRAW;\n-\tdata->usesDewarper_ = dewarper_ && !isRaw_;\n \n \t/* YUYV8_2X8 is required on the ISP source path pad for YUV output. */\n \tif (!isRaw_)\n@@ -973,15 +1016,19 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)\n \t\t\t\t\tinputCrop, sensorInfo.analogCrop);\n \t\t\t\tauto &vertexMap = dewarper_->vertexMap(cfg.stream());\n \t\t\t\tvertexMap.setSensorCrop(sensorCrop);\n+\t\t\t\tvertexMap.setTransform(config->combinedTransform());\n \t\t\t\tdata->properties_.set(properties::ScalerCropMaximum, sensorCrop);\n \n \t\t\t\t/*\n \t\t\t\t * Apply a default sensor crop that keeps the\n \t\t\t\t * aspect ratio.\n \t\t\t\t */\n-\t\t\t\tSize crop = format.size.boundedToAspectRatio(cfg.size);\n-\t\t\t\tvertexMap.setScalerCrop(crop.centeredTo(\n-\t\t\t\t\tRectangle(format.size).center()));\n+\t\t\t\tSize size = cfg.size;\n+\t\t\t\tif (transposeAfterIsp)\n+\t\t\t\t\tsize.transpose();\n+\t\t\t\tsize = sensorCrop.size().boundedToAspectRatio(size);\n+\t\t\t\tvertexMap.setScalerCrop(\n+\t\t\t\t\tsize.centeredTo(sensorCrop.center()));\n \t\t\t}\n \n \t\t\tret = mainPath_.configure(ispCfg, format);\n",
    "prefixes": [
        "v1",
        "27/33"
    ]
}