From patchwork Thu Oct 15 22:36:01 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ray Strode X-Patchwork-Id: 28468 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 AC934C3305 for ; Thu, 15 Oct 2026 22:36:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6E24568E1A; Fri, 16 Oct 2026 00:36:18 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="r0l7CXsQ"; dkim-atps=neutral Received: from mail-vs1-xe36.google.com (mail-vs1-xe36.google.com [IPv6:2607:f8b0:4864:20::e36]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0633968E0E for ; Fri, 16 Oct 2026 00:36:16 +0200 (CEST) Received: by mail-vs1-xe36.google.com with SMTP id ada2fe7eead31-7bec5112765so1220370137.1 for ; Thu, 15 Oct 2026 15:36:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792103775; x=1792708575; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to:content-type; bh=tpQG5TITpEcD3UodzKOR9pLgXTFAPmutRocyMUkOoMo=; b=r0l7CXsQp8bND44ISCh3TRFIsYYVtLR+dKwCzJjBJLB2+LeRgQKM+aP53PPRFpkPhR Mr/TIuINJ7ZNXOtRaTGAuc6SGdKjwuYyVvr0NpStZctmWdHRFGvFTFO9pDVUQ7fBf/td 5anWg3jeHNaCBB3BMFt8AmH2fEBkoaH6nWqYNQZmqKzHkXVl6953VTsFDD8CyyvgCnVF IGa1MxwX+ToauSXB48vA6LDah8br9MtLWbahPrIGxhjk134JAZJoAo/Z5qNxNiBzYeTh r0Q+HY72pmN92aLjJ8eXu/ZCUOA/n9SAVFCTNIEaR1Qlwussy4yBYL7J2YfqkWJf8Xld olCg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792103775; x=1792708575; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to:content-type; bh=tpQG5TITpEcD3UodzKOR9pLgXTFAPmutRocyMUkOoMo=; b=vVuHbEjJsRpP+9x7hrU8m10GrfBYzh6TkF9uVXuXSLkX+FgfKYvlPJraDRuRDlF2rB I1R5OsCHKNIN8AsPflID8zsZUQ/F0z93/5iZ3raAAaQ5LMMkciD/przJJb9hoV1m6u/P mVOlp8MJZEgr1lkgGMN98dGuxH+YuZAO+HsLbhNM2Oxq7WBw/y2h0nF9fywRpAQLBPic 9i+Vf6EdlKUtDsgjrXgJcPX+a7tqLLiowoj2tm2yLvlLHDM1VDKG4+Lur4qBe51wHpqi OyqWOuCFXTbtZcwax4alWTO43nlBMiF7ZkCq0yJ3Q7Xu1Re7QSeqoIjXABKN8eosHXoW qXUg== X-Gm-Message-State: AFq9FYKDAdPU+MnQOElrajLHMG8htIURve3gnawlUCtocZR0SX2zowwx rZmB5RwcgcoTyWbb7iuejELwa3yTqTRXVq4/E9H19pzKgdrHoCnNgLXpsFofWTTN8di/Fw== X-Gm-Gg: AYBFou1flOnoZCPo9GQXCRS0XKIXfClCUm6PebiIVhebDdWkJEuuem0K9JZWGUBzoCU i4IfQEHqRwuGL1nfi/ncVgs2O2g6dZPl7zkuzyYT7x61eK8dF8TKK/VOXDDfUopjCwfQDh9P4I0 HAzc8oNqKv8u6bQPNkNvSS1XSaAxlUjnsXis922fDri93/+4QRx4uEQYH6RyLno0zSmh9sqxUkc QA0K9qlKoIV6RqlGWwYXwTQi5UN4+PcfLJBxa1VhVsijEg9M0sEHz0Z+QuIuaEjCxu9LLfjsOlO iU2emt8BCyynj5Ppqn/llHRl6s6ljANhbZKcfMU6MeeVuIgJrs/LBA34AoXG864bAcyfwrMVWSa wJjtErN7Bs8CbyhYB33VGdDeKa1iqawyGQ5HPe7eJS+W3BPv49+IO0sSgKvgKk+KyRteCacm8M9 40lvAMI+xzD6T2NZUQ6rUNMcvuIpHMemT1+fkurR+KI9QWiWk+N4cjjETtxnBTTx1kA/81orkbc GTUdFnMt31OFmw8uyu7kJap8oAy X-Received: by 2002:a05:6102:418b:b0:7c3:2f13:d905 with SMTP id ada2fe7eead31-7cb3668b831mr7602952137.3.1792103774689; Thu, 15 Oct 2026 15:36:14 -0700 (PDT) Received: from localhost ([2601:19c:4a00:897d:3391:ed89:5571:41a8]) by smtp.gmail.com with ESMTPSA id ada2fe7eead31-7cc198794desm909192137.9.2026.10.15.15.36.14 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 15 Oct 2026 15:36:14 -0700 (PDT) From: Ray Strode To: libcamera-devel@lists.libcamera.org Subject: [PATCH v2 1/4] software-isp: Center the GPU image crop Date: Thu, 15 Oct 2026 18:36:01 -0400 Message-ID: <20261015223604.311616-2-halfline@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261015223604.311616-1-halfline@gmail.com> References: <20261015194335.287960-1-halfline@gmail.com> <20261015223604.311616-1-halfline@gmail.com> MIME-Version: 1.0 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" From: Ray Strode The GPU debayer path scales the image to fill a requested output size and crops any excess image area. Its projection positions the scaled image against one side of the output, so aspect-ratio changes and Bayer border removal shift the visible scene. The series aligns rendering and metering with a centered sensor crop. This commit centers the projection on the output region in sensor coordinates, distributing the crop across both sides. Signed-off-by: Ray Strode --- .../internal/software_isp/debayer_geometry.h | 41 +++++++++++++++++++ .../internal/software_isp/meson.build | 1 + src/libcamera/software_isp/debayer_egl.cpp | 21 +++------- 3 files changed, 47 insertions(+), 16 deletions(-) create mode 100644 include/libcamera/internal/software_isp/debayer_geometry.h diff --git a/include/libcamera/internal/software_isp/debayer_geometry.h b/include/libcamera/internal/software_isp/debayer_geometry.h new file mode 100644 index 00000000..6c10ecb8 --- /dev/null +++ b/include/libcamera/internal/software_isp/debayer_geometry.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026, Red Hat Inc. + * + * Geometry for scaling and cropping debayered images + */ + +#pragma once + +#include +#include + +#include + +namespace libcamera { + +/* + * Return a column-major projection matrix for an input-sized viewport and an + * output-sized framebuffer. Scale to fill the output, centering the crop on + * the input. All sizes must be nonzero, with nativeSize contained in inputSize. + */ +inline std::array debayerProjection(const Size &inputSize, + const Size &nativeSize, + const Size &outputSize) +{ + float scale = std::max(static_cast(outputSize.width) / nativeSize.width, + static_cast(outputSize.height) / nativeSize.height); + + /* The framebuffer center is output/viewport - 1 in normalized coordinates. */ + float transX = static_cast(outputSize.width) / inputSize.width - 1.0f; + float transY = static_cast(outputSize.height) / inputSize.height - 1.0f; + + return { + scale, 0, 0, 0, + 0, scale, 0, 0, + 0, 0, 1, 0, + transX, transY, 0, 1, + }; +} + +} /* namespace libcamera */ diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build index df7c3b97..e18597ed 100644 --- a/include/libcamera/internal/software_isp/meson.build +++ b/include/libcamera/internal/software_isp/meson.build @@ -2,6 +2,7 @@ libcamera_internal_headers += files([ 'benchmark.h', + 'debayer_geometry.h', 'debayer_params.h', 'software_isp.h', 'swisp_stats.h', diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp index 300822f9..1a7ad25c 100644 --- a/src/libcamera/software_isp/debayer_egl.cpp +++ b/src/libcamera/software_isp/debayer_egl.cpp @@ -9,7 +9,7 @@ #include "debayer_egl.h" -#include +#include #include #include #include @@ -24,6 +24,7 @@ #include "libcamera/internal/formats.h" #include "libcamera/internal/framebuffer.h" +#include "libcamera/internal/software_isp/debayer_geometry.h" #include "../shaders/glsl_shaders.h" @@ -388,20 +389,8 @@ void DebayerEGL::setShaderVariableValues(eGLImage &eglImageIn, const DebayerPara GLfloat Step[] = { static_cast(bytesPerPixel_) / (inputConfig_.stride - 1), 1.0f / (height_ - 1) }; GLfloat Stride = (GLfloat)width_ / (shaderStridePixels_ / bytesPerPixel_); - /* - * Scale the output size from the native size the algorithm produces for - * the input size. Keep the aspect ratio and prefer cropping over black - * bars. - */ - GLfloat scale = std::max((GLfloat)outputSize_.width / nativeOutputSize_.width, - (GLfloat)outputSize_.height / nativeOutputSize_.height); - GLfloat trans = -(1.0f - scale); - GLfloat projMatrix[] = { - scale, 0, 0, 0, - 0, scale, 0, 0, - 0, 0, 1, 0, - trans, trans, 0, 1 - }; + const std::array projMatrix = + debayerProjection({ width_, height_ }, nativeOutputSize_, outputSize_); /* Static const coordinates */ static const GLfloat vcoordinates[4][2] = { { -1.0f, -1.0f }, @@ -445,7 +434,7 @@ void DebayerEGL::setShaderVariableValues(eGLImage &eglImageIn, const DebayerPara glUniform2fv(textureUniformSize_, 1, imgSize); glUniform2fv(textureUniformStep_, 1, Step); glUniform1f(textureUniformStrideFactor_, Stride); - glUniformMatrix4fv(textureUniformProjMatrix_, 1, GL_FALSE, projMatrix); + glUniformMatrix4fv(textureUniformProjMatrix_, 1, GL_FALSE, projMatrix.data()); LOG(Debayer, Debug) << "vertexIn " << attributeVertex_ << " textureIn " << attributeTexture_ << " tex_y " << textureUniformBayerDataIn_