From patchwork Thu Aug 6 16:36:35 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9264 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 D164ABD86F for ; Thu, 6 Aug 2020 16:36:52 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9EF5260552; Thu, 6 Aug 2020 18:36:52 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="HER4ZhkA"; dkim-atps=neutral Received: from mail-wr1-x42c.google.com (mail-wr1-x42c.google.com [IPv6:2a00:1450:4864:20::42c]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2E77860392 for ; Thu, 6 Aug 2020 18:36:50 +0200 (CEST) Received: by mail-wr1-x42c.google.com with SMTP id z18so41057293wrm.12 for ; Thu, 06 Aug 2020 09:36:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=+ls6Qseo3A4UXMXVxoS7O1JGhZWS6PpmWFM/xwwZuck=; b=HER4ZhkAT119zwLLMeGX368pTs9Hd5WGps66w6WFdr73exQq9oZJrsKGR1Z0RK3c7p OtQyHvQIkXQtvspi60ADO6k8bqy+vcUBctGu8M3f3/RlOnZQmSCW4bOXlOl5DKGDvdDd /am3hNTX6SpD/kZEk/qkH4tLhgeDBcLRPFSN9kRaowK/GHxducaZfDzWjbFBu+y8Kn0G BKDSTlbAty4yQwHYBFjcaZNHVwAflE1aeEly7XnmVEl16GYnM6RfpgcuSjvq8Cvls7Xt u6bY8CpfPhw+F6gbp2ACqL1enDWzsZupGoqzsY00mJeyNyO1XGDNauLNgqtznQXdK4o6 Va1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=+ls6Qseo3A4UXMXVxoS7O1JGhZWS6PpmWFM/xwwZuck=; b=msu+s7eTytiL4P9XfhoyRfKA7ixAu2n0v08cLWxSjRnQofT2rMfe/xYqpUJGyPIoGG 2SdErbs1xTqfY4clVE8Z8oS0YsimyH5BnA2+zFSb1ToXuSKrr5yqiqoOppMl/Y0SZeAq rtd27ijsKVYIvyFJ3fM2IfzVkEsy3X+/yEIJYCe/U5SiJxxw+8Y3xSGLlVvS9CRrzx+8 Bl1HMv+XIdrNX1JOm18B1hJT/xTSd78dQua56799KnKLAzcVBAvUCQchLjVYTfz2SJ1w IgEFOy0R8WLmpxRFEOs4p+uJx+2K/TED7xZxNOEjiWIy7Yr1ijbzxpavqbDH91WSaRzo q1dQ== X-Gm-Message-State: AOAM530oDWom2vBn/Qdi0NReL7Ykf5bBOyoPVY/mJ5IiE0pZ75H/7n8K 6iP8fFYilNhJ+BFMlZT+Ot6YLghfg/+SDg== X-Google-Smtp-Source: ABdhPJwoaOnG5bMjS50sAkUhjIw3B7Ol22iAlieQzEBsyKUC3V5Vyg2xQaCiIN8vUvZkRrTq05yvAQ== X-Received: by 2002:adf:82f6:: with SMTP id 109mr8976993wrc.25.1596731809059; Thu, 06 Aug 2020 09:36:49 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id e5sm7405076wrc.37.2020.08.06.09.36.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Aug 2020 09:36:48 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Thu, 6 Aug 2020 17:36:35 +0100 Message-Id: <20200806163639.12971-2-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200806163639.12971-1-david.plowman@raspberrypi.com> References: <20200806163639.12971-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 1/5] libcamera: Add Transform enum to represet 2d plane transforms. 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" We implement 2d transforms as an enum class with 8 elements, consisting of the usual 2d plane transformations (flips, rotations etc.). The transform is made up of 3 bits, indicating whether the transform includes: a transpose, a horizontal flip (mirror) and a vertical flip. --- include/libcamera/meson.build | 1 + include/libcamera/transform.h | 58 ++++++++++++++++++++++++ src/libcamera/meson.build | 1 + src/libcamera/transform.cpp | 83 +++++++++++++++++++++++++++++++++++ 4 files changed, 143 insertions(+) create mode 100644 include/libcamera/transform.h create mode 100644 src/libcamera/transform.cpp diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index cdb8e03..7fae5e5 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -19,6 +19,7 @@ libcamera_public_headers = files([ 'span.h', 'stream.h', 'timer.h', + 'transform.h', ]) include_dir = join_paths(libcamera_include_dir, 'libcamera') diff --git a/include/libcamera/transform.h b/include/libcamera/transform.h new file mode 100644 index 0000000..658beb9 --- /dev/null +++ b/include/libcamera/transform.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Raspberry Pi (Trading) Limited + * + * transform.h - Implementation of 2d plane transforms + */ + +#ifndef __LIBCAMERA_TRANSFORM_H__ +#define __LIBCAMERA_TRANSFORM_H__ + +#include + +namespace libcamera { + +enum class Transform : int { + Identity = 0, + Rot0 = Identity, + HFlip = 1, + VFlip = 2, + HVFlip = HFlip | VFlip, + Rot180 = HVFlip, + Transpose = 4, + Rot270 = HFlip | Transpose, + Rot90 = VFlip | Transpose, + Rot180Transpose = HFlip | VFlip | Transpose +}; + +constexpr Transform operator&(Transform t0, Transform t1) +{ + return static_cast(static_cast(t0) & static_cast(t1)); +} + +constexpr Transform operator|(Transform t0, Transform t1) +{ + return static_cast(static_cast(t0) | static_cast(t1)); +} + +constexpr Transform operator^(Transform t0, Transform t1) +{ + return static_cast(static_cast(t0) ^ static_cast(t1)); +} + +Transform operator*(Transform t0, Transform t1); + +Transform operator-(Transform t); + +constexpr bool operator!(Transform t) +{ + return t == Transform::Identity; +} + +Transform transformFromRotation(int angle, bool *success = nullptr); + +std::string transformToString(Transform t); + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_TRANSFORM_H__ */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index bada45b..b46247d 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -44,6 +44,7 @@ libcamera_sources = files([ 'sysfs.cpp', 'thread.cpp', 'timer.cpp', + 'transform.cpp', 'utils.cpp', 'v4l2_controls.cpp', 'v4l2_device.cpp', diff --git a/src/libcamera/transform.cpp b/src/libcamera/transform.cpp new file mode 100644 index 0000000..5f00a5c --- /dev/null +++ b/src/libcamera/transform.cpp @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Raspberry Pi (Trading) Limited + * + * transform.cpp - implementation of 2d plane transforms. + */ + +#include + +/** + * \file transform.h + * \brief Enum to represent a 2d plane transforms. + */ + +namespace libcamera { + +Transform operator*(Transform t0, Transform t1) +{ + /* + * Reorder the operations so that we imagine doing t1's transpose + * (if any) after t0's flips. The effect is to swap t0's hflips for + * vflips and vice versa, after which we can just xor all the bits. + */ + Transform reordered = t0; + if (!!(t1 & Transform::Transpose)) + reordered = (t0 & Transform::Transpose) | + (!!(t0 & Transform::HFlip) ? Transform::VFlip : Transform::Identity) | + (!!(t0 & Transform::VFlip) ? Transform::HFlip : Transform::Identity); + + return reordered ^ t1; +} + +Transform operator-(Transform t) +{ + /* All are self-inverses, except for Rot270 and Rot90. */ + static const Transform inverses[] = { + Transform::Identity, Transform::HFlip, Transform::VFlip, Transform::HVFlip, + Transform::Transpose, Transform::Rot90, Transform::Rot270, Transform::Rot180Transpose + }; + + return inverses[static_cast(t)]; +} + +Transform transformFromRotation(int angle, bool *success) +{ + angle = angle % 360; + if (angle < 0) + angle += 360; + + if (success != nullptr) + *success = true; + + if (angle == 0) + return Transform::Identity; + else if (angle == 90) + return Transform::Rot90; + else if (angle == 180) + return Transform::Rot180; + else if (angle == 270) + return Transform::Rot270; + else if (success != nullptr) + *success = false; + + return Transform::Identity; +} + +std::string transformToString(Transform t) +{ + static const char *strings[] = { + "identity", + "hflip", + "vflip", + "hvflip", + "transpose", + "rot270", + "rot90", + "rot180transpose" + }; + + return strings[static_cast(t)]; +} + +} /* namespace libcamera */ From patchwork Thu Aug 6 16:36:36 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9265 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 6519ABD86F for ; Thu, 6 Aug 2020 16:36:53 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id EC81760DDE; Thu, 6 Aug 2020 18:36:52 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="KUfxkrYw"; dkim-atps=neutral Received: from mail-wr1-x436.google.com (mail-wr1-x436.google.com [IPv6:2a00:1450:4864:20::436]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 52D8560392 for ; Thu, 6 Aug 2020 18:36:51 +0200 (CEST) Received: by mail-wr1-x436.google.com with SMTP id a15so44614846wrh.10 for ; Thu, 06 Aug 2020 09:36:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=7E2mCFX8dC5oKndfcsoqj9B2tHhHcXoPxYv7Kws94nY=; b=KUfxkrYwgE3xaqxVlBobpLnYMjmBkF7ELDHGLb9ceh8UVdqNvF/CvcZQeqqds3Uelr O4pLWJOeLAEzBlWDXJRelHRvX4R9Cl+yLUcNEsolzx/UX1F83gRnebEagn6msOKeFyQ9 hsFnFTS53khCweXscz+E2rHuE5TYXhY89I2cXsPdVOlrfCLi908mo7BoAKXPRhOZPb1s /vPDvOnyOhXQpchZko1sI7NJnKTnRtcH5QTnK+UPOGLXsfwlwRdW2yQMiC5gsDqrlzgu ymGKbN+tnU0IKkvTLx0bT7feqZpw+XWB/Z1Skn/MkWw4dpFCol82akMC94W5Z2VChKOp JuEA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=7E2mCFX8dC5oKndfcsoqj9B2tHhHcXoPxYv7Kws94nY=; b=XIJtFUjqD0JadupSzy3YCeFENnhrTMdCHqo7g+7/dr332k2pU+9TxagenLJrF/g2VN GHrLt01pbagD7eIgkNg/QJWUyYE5VgW/s6uoqIToT1oUwIO2pBdPTCoXht9tGdOUv/Qf lZWKQBdjv2MLNnGsP5fXcMBJi34gKmfVGy4VmhwXjtCuVvFXpI632KO0l6golBPNQPgo mHvtFk27w1yXq3TdQdNkjpd2X+K01y6zXopCmm1h5+iAA6eLyEsFILiaEoosue9S7Lzh bvIrcNf9DoILL5hf3nvJ6a914ZEqmKRCFQMhtULjmCqAjMK5YY2Y2BoC5EYqTmEB08F7 qxdg== X-Gm-Message-State: AOAM532uBx8sKe1oiw9sj3rXNfEKpIyljdlJFURIi7ChvQNWGimO7GgG om8wx+WvlC+mWsojT0ae4GGmjzvwE7S06Q== X-Google-Smtp-Source: ABdhPJx2CRkyfbp70mVrYd3lFBbblvfufNiJY1oXo2mfTqBvg/ODvua3jqL8ozcuKFW1bju7So9Tfw== X-Received: by 2002:a5d:67d2:: with SMTP id n18mr8006616wrw.214.1596731810466; Thu, 06 Aug 2020 09:36:50 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id e5sm7405076wrc.37.2020.08.06.09.36.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Aug 2020 09:36:49 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Thu, 6 Aug 2020 17:36:36 +0100 Message-Id: <20200806163639.12971-3-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200806163639.12971-1-david.plowman@raspberrypi.com> References: <20200806163639.12971-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 2/5] libcamera: Add user Transform to CameraConfiguration 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" Add a field to the CameraConfiguration to represent a 2d transform requested by the application. All pipeline handlers are amended to coerce this to the Identity, marking the configuration as "adjusted" if something different had been requested. Pipeline handlers that support Transforms can be amended subsequently. --- include/libcamera/camera.h | 3 +++ src/libcamera/camera.cpp | 2 +- src/libcamera/pipeline/ipu3/ipu3.cpp | 5 +++++ src/libcamera/pipeline/raspberrypi/raspberrypi.cpp | 5 +++++ src/libcamera/pipeline/rkisp1/rkisp1.cpp | 5 +++++ src/libcamera/pipeline/simple/simple.cpp | 5 +++++ src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 5 +++++ src/libcamera/pipeline/vimc/vimc.cpp | 5 +++++ 8 files changed, 34 insertions(+), 1 deletion(-) diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index 48d88d6..dedc1c6 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -17,6 +17,7 @@ #include #include #include +#include namespace libcamera { @@ -61,6 +62,8 @@ public: bool empty() const; std::size_t size() const; + Transform transform; + protected: CameraConfiguration(); diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index 820fa1e..4282a02 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -93,7 +93,7 @@ LOG_DECLARE_CATEGORY(Camera) * \brief Create an empty camera configuration */ CameraConfiguration::CameraConfiguration() - : config_({}) + : transform(Transform::Identity), config_({}) { } diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index d931ed3..a9a82a4 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -138,6 +138,11 @@ CameraConfiguration::Status IPU3CameraConfiguration::validate() if (config_.empty()) return Invalid; + if (transform != Transform::Identity) { + transform = Transform::Identity; + status = Adjusted; + } + /* Cap the number of entries to the available streams. */ if (config_.size() > IPU3_MAX_STREAMS) { config_.resize(IPU3_MAX_STREAMS); diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index eeaf335..236aa5c 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -400,6 +400,11 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() if (config_.empty()) return Invalid; + if (transform != Transform::Identity) { + transform = Transform::Identity; + status = Adjusted; + } + unsigned int rawCount = 0, outCount = 0, count = 0, maxIndex = 0; std::pair outSize[2]; Size maxSize; diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index b7609cb..002f8e5 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -478,6 +478,11 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate() if (config_.empty()) return Invalid; + if (transform != Transform::Identity) { + transform = Transform::Identity; + status = Adjusted; + } + /* Cap the number of entries to the available streams. */ if (config_.size() > 1) { config_.resize(1); diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp index eb72e3b..10223a9 100644 --- a/src/libcamera/pipeline/simple/simple.cpp +++ b/src/libcamera/pipeline/simple/simple.cpp @@ -438,6 +438,11 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate() if (config_.empty()) return Invalid; + if (transform != Transform::Identity) { + transform = Transform::Identity; + status = Adjusted; + } + /* Cap the number of entries to the available streams. */ if (config_.size() > 1) { config_.resize(1); diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp index bc892ec..fd14248 100644 --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp @@ -108,6 +108,11 @@ CameraConfiguration::Status UVCCameraConfiguration::validate() if (config_.empty()) return Invalid; + if (transform != Transform::Identity) { + transform = Transform::Identity; + status = Adjusted; + } + /* Cap the number of entries to the available streams. */ if (config_.size() > 1) { config_.resize(1); diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp index cf244f1..bb791d6 100644 --- a/src/libcamera/pipeline/vimc/vimc.cpp +++ b/src/libcamera/pipeline/vimc/vimc.cpp @@ -130,6 +130,11 @@ CameraConfiguration::Status VimcCameraConfiguration::validate() if (config_.empty()) return Invalid; + if (transform != Transform::Identity) { + transform = Transform::Identity; + status = Adjusted; + } + /* Cap the number of entries to the available streams. */ if (config_.size() > 1) { config_.resize(1); From patchwork Thu Aug 6 16:36:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9266 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 76C6ABD86F for ; Thu, 6 Aug 2020 16:36:54 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3F11460DF1; Thu, 6 Aug 2020 18:36:54 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="KSDU7Hyx"; dkim-atps=neutral Received: from mail-wr1-x42d.google.com (mail-wr1-x42d.google.com [IPv6:2a00:1450:4864:20::42d]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 76E0560392 for ; Thu, 6 Aug 2020 18:36:52 +0200 (CEST) Received: by mail-wr1-x42d.google.com with SMTP id a14so44655794wra.5 for ; Thu, 06 Aug 2020 09:36:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=defdvotbVHVlh1WFgVd8CY4nNYsHR1HxniuD+N+F9aI=; b=KSDU7HyxdOuGD+qeutuGl7HW/nPtrA/TmIMZqNACkfCpsVcojWGkDNEpgCxPlLBsiK 2bJER2DVqoe/JGDuPTCcnSOtPIGSzYnRpwjfDYDX65mZPx7aT4JJD78sXmaeo/8ufywB z/ng4CgkdOd/00epR8p6B9ddJti8DJzQCj5t+nIrcRXZqlcjKtI+xjDtkD1wugDSBGTs FJVKr0bo07e7nSZA25YLPZPZtIO4ejEZooqYTj6vPV/oi16jJ/BfugpCkDNQNFp1Azx5 zQVifpDrKrmRjx1+PY90DKMlj7OaHfpnMu9bvnRW3DlK19L6Agc4WrdZupnGCrOh9sW+ wwqA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=defdvotbVHVlh1WFgVd8CY4nNYsHR1HxniuD+N+F9aI=; b=tQVjBufHo4kcdP2KEr9sOv6CpYg5Cb2ASREumez4RSwgEZPmY/sWIKorIPL6PvY3ud FlBQYnUNzuUz/wk+4Gu2WmkmfPkze+hQLIJLPG7ECyoWwdy3bZf2I0oCI1WkxzMYMw+4 s9pAjORX9OEiKokuIk9jCdWtQI9+1XZFSRMVoBd02S6NTn0dhyWHFsMKsCBXTe4jT1JQ 03v1S6pWC0QtdciaPfsmFVIpCujBfOuEu6dm8m80ARgd7TPoPCd7JkY+EB65O4c8/pcJ uxftAYDL+NjqKYrEgIMgZYaDgLZVJMSqJ1WgFIlk1/in68OJ46yGWkAIYrSywcOCHccA ltPg== X-Gm-Message-State: AOAM533MUVP3AvIO09DmJeeOvRIEVSQwg6jJn6PR+wwwVxkRL7EUy1Zw YcIXDrf60Hke0LcLqrAWiknFMKjceDmR7Q== X-Google-Smtp-Source: ABdhPJyaTpJj38JYqGtoFqxCaq/9+ePcTbVCxu4tBIGmBX+W3t6WhukFO0ysWYCPVz6iVkN1QRrqnw== X-Received: by 2002:adf:ed0c:: with SMTP id a12mr8067486wro.24.1596731811722; Thu, 06 Aug 2020 09:36:51 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id e5sm7405076wrc.37.2020.08.06.09.36.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Aug 2020 09:36:50 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Thu, 6 Aug 2020 17:36:37 +0100 Message-Id: <20200806163639.12971-4-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200806163639.12971-1-david.plowman@raspberrypi.com> References: <20200806163639.12971-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 3/5] libcamera: raspberrypi: Set camera flips correctly from user transform 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" The Raspberry Pi pipeline handler allows all transforms except those involving a transpose. The user transform is combined with any inherent rotation of the camera, and the camera's H and V flip bits are set accordingly. --- .../pipeline/raspberrypi/raspberrypi.cpp | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index 236aa5c..9d183e3 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -324,6 +324,8 @@ public: uint32_t expectedSequence_; bool sensorMetadata_; + Transform transform_; + /* * All the functions in this class are called from a single calling * thread. So, we do not need to have any mutex to protect access to any @@ -400,8 +402,9 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() if (config_.empty()) return Invalid; - if (transform != Transform::Identity) { - transform = Transform::Identity; + /* We cannot do Transforms with a transpose in them. */ + if (!!(transform & Transform::Transpose)) { + transform = transform ^ Transform::Transpose; status = Adjusted; } @@ -610,6 +613,9 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config) for (auto const stream : data->streams_) stream->reset(); + /* We will want to know the transform requested by the application. */ + data->transform_ = config->transform; + Size maxSize, sensorSize; unsigned int maxIndex = 0; bool rawStream = false; @@ -1174,8 +1180,14 @@ int RPiCameraData::configureIPA() /* Configure the H/V flip controls based on the sensor rotation. */ ControlList ctrls(unicam_[Unicam::Image].dev()->controls()); int32_t rotation = sensor_->properties().get(properties::Rotation); - ctrls.set(V4L2_CID_HFLIP, static_cast(!!rotation)); - ctrls.set(V4L2_CID_VFLIP, static_cast(!!rotation)); + bool success; + Transform combined = transform_ * transformFromRotation(rotation, &success); + if (!success) { + LOG(RPI, Error) << "Invalid rotation: " << rotation; + return -EINVAL; + } + ctrls.set(V4L2_CID_HFLIP, static_cast(!!(combined & Transform::HFlip))); + ctrls.set(V4L2_CID_VFLIP, static_cast(!!(combined & Transform::VFlip))); unicam_[Unicam::Image].dev()->setControls(&ctrls); } From patchwork Thu Aug 6 16:36:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9267 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 DFA42BD86F for ; Thu, 6 Aug 2020 16:36:55 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A86FE60BF7; Thu, 6 Aug 2020 18:36:55 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="Treo+me8"; dkim-atps=neutral Received: from mail-wr1-x42e.google.com (mail-wr1-x42e.google.com [IPv6:2a00:1450:4864:20::42e]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9CB4C60554 for ; Thu, 6 Aug 2020 18:36:53 +0200 (CEST) Received: by mail-wr1-x42e.google.com with SMTP id a14so44655845wra.5 for ; Thu, 06 Aug 2020 09:36:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=OauAAyf3Ed6cD9eIYOvRmW8YEeE/iHxDk0SCcOD30Kc=; b=Treo+me84xv9sKUNUTlxivaYT0HrdF1CXoRoJdhjY4fSvk2VfUCFPED036OIkAPCGV MsuznhriKKAFfy6Uu1M5fywJMjFT/KVdj2An4GLt/Ept1c8TCX6TTTtMwom6zleE2g2Z di7t1fZosgcfJ5d0X4Li4k36c4XvKCZnOONPgyjvFSHTnGY3jsvvdMWjI8+54OiKx8J0 RKV+ljxxEONlsQnELq696jpr8FqgkHT9nq6WEbysmgQlD3XQWoRqDAPLlQ6rD7M+3Ki9 U3mgsE70+h28qbbXgCRJywP7lrpSeIqrFJPq338zjkLZg3NO+Ld9nu1n/UON4jh8lAx2 IOtA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=OauAAyf3Ed6cD9eIYOvRmW8YEeE/iHxDk0SCcOD30Kc=; b=VId4ASbnOdnI4t8XCBk+QiaIr9zVKEw1qYfJ2NnVKOdvB2hrSGfHM89iR3aSIsN5Xm tZjhl1xlfytfXBVIq12VvrRhDa7niqP8fV0QFfr8wL8qGxuIF57iQae5TfN/DVKhFu1P ckGDKENI6xRVc5jZCtQrEOqWFIkUqq/RyIYGxerC2o6hAtvrFTT8fq/9Q/Gz6226fiSN es8Zdh+cbc+fuRzCb+BNdUULr1CYnxYi5Nw8UnjsRQrmDLmGy7rxJNW/50OUUB4aeo0M gcIFEyxDBWJM6PchWUCpfLt5PMvT/IhZjSGWzESmnnp9o7Pj8JdazhZoQFwAK1Sj7laS uNzg== X-Gm-Message-State: AOAM53012eU8v4Y1fzobc/e2Jl0qpQZPrvn1LoppwANk+w3SGTe14hmA jWNaRh1AayiMSyB0+jYvYQU6VjwOuPY+gw== X-Google-Smtp-Source: ABdhPJyShsoj4zkm1CUNG3iL8pb/MAxXJgYgrmLbxikNVhvZtj4dHFRXgDs9c8ku/tH3dNVr2OL/6g== X-Received: by 2002:a5d:460c:: with SMTP id t12mr8552280wrq.334.1596731812874; Thu, 06 Aug 2020 09:36:52 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id e5sm7405076wrc.37.2020.08.06.09.36.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Aug 2020 09:36:52 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Thu, 6 Aug 2020 17:36:38 +0100 Message-Id: <20200806163639.12971-5-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200806163639.12971-1-david.plowman@raspberrypi.com> References: <20200806163639.12971-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 4/5] libcamera: raspberrypi: Plumb user transform through to IPA 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" This commit plumbs the user transform from the Raspberry Pi pipeline handler through to the IPA. Once in the IPA we add it to the CameraMode description, so that it becomes automatically available to all the individual control algorithms. The IPA configure method has to be reordered just a little so as to fill in the transform in the camera mode before calling SwitchMode. --- src/ipa/raspberrypi/controller/camera_mode.h | 4 ++ src/ipa/raspberrypi/raspberrypi.cpp | 48 +++++++++++-------- .../pipeline/raspberrypi/raspberrypi.cpp | 5 +- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/ipa/raspberrypi/controller/camera_mode.h b/src/ipa/raspberrypi/controller/camera_mode.h index 875bab3..920f11b 100644 --- a/src/ipa/raspberrypi/controller/camera_mode.h +++ b/src/ipa/raspberrypi/controller/camera_mode.h @@ -6,6 +6,8 @@ */ #pragma once +#include + // Description of a "camera mode", holding enough information for control // algorithms to adapt their behaviour to the different modes of the camera, // including binning, scaling, cropping etc. @@ -33,6 +35,8 @@ struct CameraMode { double noise_factor; // line time in nanoseconds double line_length; + // any camera transform *not* reflected already in the camera tuning + libcamera::Transform transform; }; #ifdef __cplusplus diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp index 3747208..0b36d57 100644 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ b/src/ipa/raspberrypi/raspberrypi.cpp @@ -232,6 +232,33 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo, /* Re-assemble camera mode using the sensor info. */ setMode(sensorInfo); + /* + * The ipaConfig.data always gives us the user transform first. Note that + * this will always make the LS table pointer (if present) element 1. + */ + mode_.transform = static_cast(ipaConfig.data[0]); + + /* Store the lens shading table pointer and handle if available. */ + if (ipaConfig.operation & RPI_IPA_CONFIG_LS_TABLE) { + /* Remove any previous table, if there was one. */ + if (lsTable_) { + munmap(lsTable_, MAX_LS_GRID_SIZE); + lsTable_ = nullptr; + } + + /* Map the LS table buffer into user space (now element 1). */ + lsTableHandle_ = FileDescriptor(ipaConfig.data[1]); + if (lsTableHandle_.isValid()) { + lsTable_ = mmap(nullptr, MAX_LS_GRID_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, lsTableHandle_.fd(), 0); + + if (lsTable_ == MAP_FAILED) { + LOG(IPARPI, Error) << "dmaHeap mmap failure for LS table."; + lsTable_ = nullptr; + } + } + } + /* Pass the camera mode to the CamHelper to setup algorithms. */ helper_->SetCameraMode(mode_); @@ -280,27 +307,6 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo, } lastMode_ = mode_; - - /* Store the lens shading table pointer and handle if available. */ - if (ipaConfig.operation & RPI_IPA_CONFIG_LS_TABLE) { - /* Remove any previous table, if there was one. */ - if (lsTable_) { - munmap(lsTable_, MAX_LS_GRID_SIZE); - lsTable_ = nullptr; - } - - /* Map the LS table buffer into user space. */ - lsTableHandle_ = FileDescriptor(ipaConfig.data[0]); - if (lsTableHandle_.isValid()) { - lsTable_ = mmap(nullptr, MAX_LS_GRID_SIZE, PROT_READ | PROT_WRITE, - MAP_SHARED, lsTableHandle_.fd(), 0); - - if (lsTable_ == MAP_FAILED) { - LOG(IPARPI, Error) << "dmaHeap mmap failure for LS table."; - lsTable_ = nullptr; - } - } - } } void IPARPi::mapBuffers(const std::vector &buffers) diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index 9d183e3..2fdf79f 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -1141,6 +1141,9 @@ int RPiCameraData::configureIPA() entityControls.emplace(0, unicam_[Unicam::Image].dev()->controls()); entityControls.emplace(1, isp_[Isp::Input].dev()->controls()); + /* Always send the user transform to the IPA. */ + ipaConfig.data = { static_cast(transform_) }; + /* Allocate the lens shading table via dmaHeap and pass to the IPA. */ if (!lsTable_.isValid()) { lsTable_ = dmaHeap_.alloc("ls_grid", MAX_LS_GRID_SIZE); @@ -1149,7 +1152,7 @@ int RPiCameraData::configureIPA() /* Allow the IPA to mmap the LS table via the file descriptor. */ ipaConfig.operation = RPI_IPA_CONFIG_LS_TABLE; - ipaConfig.data = { static_cast(lsTable_.fd()) }; + ipaConfig.data.push_back(static_cast(lsTable_.fd())); } CameraSensorInfo sensorInfo = {}; From patchwork Thu Aug 6 16:36:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 9268 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 88DD2BD86F for ; Thu, 6 Aug 2020 16:36:58 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 55DE960BF7; Thu, 6 Aug 2020 18:36:58 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="bwVCpo/u"; dkim-atps=neutral Received: from mail-wm1-x32a.google.com (mail-wm1-x32a.google.com [IPv6:2a00:1450:4864:20::32a]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8F5E860E00 for ; Thu, 6 Aug 2020 18:36:54 +0200 (CEST) Received: by mail-wm1-x32a.google.com with SMTP id x5so9417498wmi.2 for ; Thu, 06 Aug 2020 09:36:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=pU4KCuTRMlbv0WdNJxhxE+59YkBpPb4btW7ceGvrAzo=; b=bwVCpo/ufljfPzwWZkOTeEfkDx9yaF/MKF9JfNKTZNryzOSilp6lZnhfl7uR8Cmn/J K6Tma8W6KufP4WMf08s95C6y0UFzmSnXgOv34Jcj9vSj8lGcHmOYKLukB2Bwygz+448l rKUioAFGJdsj4TtH1Z+WiXz/UNAhqpGSC7w9D+bLBJGl52vCdemhA/ttEvM38diH/gon r63xM9Lr9AXRKQ8gEJuygBDFn2TVu0V4wR7THNicLGgZ4xD43SCTG4uUauSAi6nEWMPV KjuJKIyZRMLBMdFmRwq7DH2QXLYkHYdZB7M5+vlW3uue/5KhVwjlBjVmu6uuyrvo+DIe y7aA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=pU4KCuTRMlbv0WdNJxhxE+59YkBpPb4btW7ceGvrAzo=; b=UF65Kd3bW9z8MSrLCTEuOX57P/XG9OfCisvzugshLNH/MK3zh5h1KGglntqol0ACzy RGSd6QltXFpUYHwGYVzrQqlj/V+ClJnjA9Fd9sCXsWl4JFrFkIkbQsOyXmkDaFFhPf40 NE+YAnS2X2Ns0rGEUurGaUov+Y4Imx0A7nXtZNnUW9yLTDHdCdXkdP/U8CW1iUawRL2+ K+vPb5JAMVaxF9UKBQQC4KOuN8YvOjkBBAvAF4SMmYkPhFUOrzAZK00/QonP+JZrUcDh bbcRP8Un8YWmcyfq0iDVb0vBxyKMhkHeXOWkqLcKGSgsNcSG0wFFMZ1ZlCQexu3XY1jB Q4GQ== X-Gm-Message-State: AOAM530BmECTZa4YoeEq5TyYeyTcVEdfOIZo7edcXjQeXhzMGfUHTXIv RS5TflVCi1D8Ptj83kMOnSHB7rUXqlYvpQ== X-Google-Smtp-Source: ABdhPJw0YLKejQph28OHGMu0hmW3YSmetRLmv6vBkdAIWXqJN4r1Y7RJwVd+i2lDrasTRJRFNviIFQ== X-Received: by 2002:a1c:678b:: with SMTP id b133mr9523240wmc.117.1596731813922; Thu, 06 Aug 2020 09:36:53 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id e5sm7405076wrc.37.2020.08.06.09.36.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Aug 2020 09:36:53 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Thu, 6 Aug 2020 17:36:39 +0100 Message-Id: <20200806163639.12971-6-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200806163639.12971-1-david.plowman@raspberrypi.com> References: <20200806163639.12971-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 5/5] libcamera: ipa: raspberrypi: ALSC: Handle user transform 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" Update ALSC (Auto Lens Shading Correction) to handle correctly the user transform now passed in the camera mode. The user transform is applied directly in the sensor so the image statistics already incorporate it, and the adaptive algorithm is entirely agnostic towards it, so all we have to do is flip the calibrated tables to match. (These tables will have been calibrated without the user transform.) --- src/ipa/raspberrypi/controller/rpi/alsc.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/ipa/raspberrypi/controller/rpi/alsc.cpp b/src/ipa/raspberrypi/controller/rpi/alsc.cpp index 9df713a..fdeda21 100644 --- a/src/ipa/raspberrypi/controller/rpi/alsc.cpp +++ b/src/ipa/raspberrypi/controller/rpi/alsc.cpp @@ -184,7 +184,10 @@ void Alsc::waitForAysncThread() static bool compare_modes(CameraMode const &cm0, CameraMode const &cm1) { - // Return true if the modes crop from the sensor significantly differently. + // Return true if the modes crop from the sensor significantly differently, + // or if the user transform has changed. + if (cm0.transform != cm1.transform) + return true; int left_diff = abs(cm0.crop_x - cm1.crop_x); int top_diff = abs(cm0.crop_y - cm1.crop_y); int right_diff = fabs(cm0.crop_x + cm0.scale_x * cm0.width - @@ -429,6 +432,10 @@ void resample_cal_table(double const cal_table_in[XY], xf[i] = x - x_lo[i]; x_hi[i] = std::min(x_lo[i] + 1, X - 1); x_lo[i] = std::max(x_lo[i], 0); + if (!!(camera_mode.transform & libcamera::Transform::HFlip)) { + x_lo[i] = X - 1 - x_lo[i]; + x_hi[i] = X - 1 - x_hi[i]; + } } // Now march over the output table generating the new values. double scale_y = camera_mode.sensor_height / @@ -441,6 +448,10 @@ void resample_cal_table(double const cal_table_in[XY], double yf = y - y_lo; int y_hi = std::min(y_lo + 1, Y - 1); y_lo = std::max(y_lo, 0); + if (!!(camera_mode.transform & libcamera::Transform::VFlip)) { + y_lo = Y - 1 - y_lo; + y_hi = Y - 1 - y_hi; + } double const *row_above = cal_table_in + X * y_lo; double const *row_below = cal_table_in + X * y_hi; for (int i = 0; i < X; i++) {