From patchwork Fri Oct 16 20:28:08 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ray Strode X-Patchwork-Id: 28473 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 1DED7C324C for ; Fri, 16 Oct 2026 20:28:37 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 951F568E39; Fri, 16 Oct 2026 22:28:33 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="RCsF61GK"; dkim-atps=neutral Received: from mail-vk1-xa32.google.com (mail-vk1-xa32.google.com [IPv6:2607:f8b0:4864:20::a32]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B1ABF689D7 for ; Fri, 16 Oct 2026 22:28:30 +0200 (CEST) Received: by mail-vk1-xa32.google.com with SMTP id 71dfb90a1353d-5e91e422859so146575e0c.2 for ; Fri, 16 Oct 2026 13:28:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792182509; x=1792787309; 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:content-type; bh=O6IMcrMQopRjnTT3QwE0YRk0e8QEhXr3Jk4x+3QRUOk=; b=RCsF61GKyqRvh1sGgCIaPaTg3MDMXUk4tjOqHkW60rmtDc7pT/C4vIoEa0Tt9x/Yz3 7XrXlhah3u/v00zWd0CktzOXZCBvZDpF1bl1wXFHj87wHnQA6cTP3SDwKpeUpu9jc+jq bfQb/tetfUmhoOgu4ep0wI3Vi1Kl6kjaFmBt/fkMoud9uwtBPv7d8qaRKNYmWVShSXKZ jR0McrTUXGoUyC7k1XCVC+8lOQvF0PpsU9153zNkLkWpvH+7HyWKyiTPM8LPncvez6gD w4jU0oD9yLE0vPqLX7brJyAZOWA2mOBLr+W77r8HapMwW8ycdnFhp4mjBelqUQ/xP54P twHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792182509; x=1792787309; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=O6IMcrMQopRjnTT3QwE0YRk0e8QEhXr3Jk4x+3QRUOk=; b=eTMvZW4wKy+32fKSZFkVMOhwJaKrTGBK0jelEGsxClVymC/jKJXE+BNDV5dKwv9drf T8/7Hiu7+OQK/W6b6l1ph1vUt7FxsbTxE5g0kWhP6o19mPOScHUr9po90LFsnrHfREuU FHJu9muA+2tm0mLlUBJKUhbKabjZjIfg4j29L3jSZW1K6XJxtlICtLgeE/c2kq3sGezd wCpJOkDnaAKz02N2ZOpmkU++Df+wjUJli3cnAIfr1dpxec+YWImtMeuzvd28NI+mYSv1 GBkyR8YxDKEuRbcilvgNvUYTb1uCJce4SaV29yc4kjTqkysaaDhFxBC3IIJKK8jNHafM aaVw== X-Gm-Message-State: AFq9FYLxUeBx/9vo3/1zWYYKnL8MBQ9SoKz2st7cNmQ/Gx0B0mgVQiUu lAPZZeKSj6Qs6D/TVDBMtlrGlJWDPkj/xjIrRFBGYV5+z5L5m6RZ/Z7NRHAItc3fncw= X-Gm-Gg: AYBFou0dlI4Q38c0jo4pIPFbqVQyjcZ9mu6WzhIiYIkY7/UI699JkiDu2X0r5PYfXgz L6l9g7M70YBXotlV1sTnqxwxDDhmek/JoBNUs33RUX9KnYdpEz9tjo1yfboFSzNwjEPAMQdBuuE QuUs635CXPeCfFZEQcS3TwrNZ72LEdVOAwef/PPNiJYxoftGObmRH5OJD7gYCm9bS4fuxShJ1M0 IAhT4ZXImsd/IVG/QNMe5/vT0WfuZvjbD674I9as1tLhqsuFACARnajnjhMAcJjNzTSpvpVUoJX rchZzSDfrWljJMLm03Q1GM2jS8rlhBPqtrr6Ae/RkV/fOqmIzaqM9J4hrXRXmGFPho4oWQ+ABym +E1OOTwHONSvcKv1uNatP/Zzpe/JDFlelh+JL37ny+lpVB9/anlrCz4zG/8umlKCG2It62L2Z1Q LpciJ2RdVMpXZugxNPoeqNQcSdeSfRFbKeMMaeJNiSZ0iEVf5MlyRJCxLtEqhQOVCCY8TKCCIqs sL1ilNWBxPLDG1KZDtHrOkR93uD X-Received: by 2002:a05:6123:4205:10b0:5d4:1193:73d3 with SMTP id 71dfb90a1353d-5eaaa64171fmr428322e0c.14.1792182509476; Fri, 16 Oct 2026 13:28:29 -0700 (PDT) Received: from localhost ([2601:19c:4a00:897d:3391:ed89:5571:41a8]) by smtp.gmail.com with ESMTPSA id 71dfb90a1353d-5ea9b2fef79sm2422500e0c.1.2026.10.16.13.28.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Oct 2026 13:28:28 -0700 (PDT) From: Ray Strode To: libcamera-devel@lists.libcamera.org Cc: kieran.bingham@ideasonboard.com, bod.linux@nxsw.ie, laurent.pinchart@ideasonboard.com Subject: [PATCH v3 01/11] software-isp: Center the GPU image crop Date: Fri, 16 Oct 2026 16:28:08 -0400 Message-ID: <20261016202818.1405645-2-halfline@gmail.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261016202818.1405645-1-halfline@gmail.com> References: <20261015223604.311616-1-halfline@gmail.com> <20261016202818.1405645-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 | 43 +++++++++++++++++++ .../internal/software_isp/meson.build | 1 + src/libcamera/software_isp/debayer_egl.cpp | 21 +++------ 3 files changed, 49 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..70ae6a8b --- /dev/null +++ b/include/libcamera/internal/software_isp/debayer_geometry.h @@ -0,0 +1,43 @@ +/* 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; + + /* clang-format off */ + return { + scale, 0, 0, 0, + 0, scale, 0, 0, + 0, 0, 1, 0, + transX, transY, 0, 1, + }; + /* clang-format on */ +} + +} /* 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_