From patchwork Sat Oct 3 15:45:59 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Alain_Cousini=C3=A9?= X-Patchwork-Id: 28393 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 71CCDBDCB2 for ; Sat, 3 Oct 2026 15:46:05 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 410AC689F4; Sat, 3 Oct 2026 17:46:04 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=laposte.net header.i=@laposte.net header.b="gOR/f7xP"; dkim-atps=neutral Received: from smtp-outgoing-2001.laposte.net (smtp-outgoing-2001.laposte.net [160.92.124.108]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E3645689EA for ; Sat, 3 Oct 2026 17:46:01 +0200 (CEST) X-mail-filterd: {"version":"1.9.7", "queueID":"4hxqkH4Hqkz10MQN", "contextId": "fe3470cc-a114-4251-ae58-2573c7baf53c"} Received: from outgoing-mail.laposte.net (localhost.localdomain [127.0.0.1]) by mlpnf0120.laposte.net (SMTP Server) with ESMTP id 4hxqkH4Hqkz10MQN for ; Sat, 3 Oct 2026 17:45:59 +0200 (CEST) X-mail-filterd: {"version":"1.9.7", "queueID":"4hxqkH3Z9wz10MQ4", "contextId": "dee69a40-012b-4cd4-90ac-4c1010eb3284"} X-lpn-mailing: LEGIT X-lpn-spamrating: 40 X-lpn-spamlevel: not-spam Received: from [192.168.1.2] (m-dj.hd.free.fr [82.67.82.126]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mlpnf0120.laposte.net (SMTP Server) with ESMTPSA id 4hxqkH3Z9wz10MQ4 for ; Sat, 3 Oct 2026 17:45:59 +0200 (CEST) Message-ID: Date: Sat, 3 Oct 2026 17:45:59 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Content-Language: fr To: libcamera-devel@lists.libcamera.org From: Alain Cousinie Subject: [PATCH] libcamera: software_isp: Add R10 and R10_CSI2P monochrome formats DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=laposte.net; s=lpn-wlmd; t=1791042361; bh=a/YJaQ+O8wQMvyKuFVd1QAwNnNCz5EiXkeaVtqqkTKg=; h=Message-ID:Date:MIME-Version:Content-Language:To:From:Subject:Content-Type:Content-Transfer-Encoding; b=gOR/f7xPXPwoKx4MmIGXJUkoLGMF+c5eDU71ZgR30Cd2sX9ONJu5WdNwjzFiZwMDteAw9n6kAh8dUO5q8nB/IZdJK2Vr6OP6wT1zft87a9A8rqP52vXsAHmTW4VcQc3FQarN1v3jjzDxWq0wwft3KwqCGmOw1aCOXvLQ2Ftg/6jh9Wu6K48seYBeiO1mLWEvgHRDStC46/Xb/ZLfZiYHKP0DDPRtbQ0NeiVyJn1OdbY7zUn7vKCmWGRJ/JURgrK+0ScdElPWUQyUn5TcWsQYamTXRc7ELJzXv49rplJhRDnfMbxSULQ32+sVkqByTtGmo8ecFZjs3iQf1V7a4Tso3w==; 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 051ad3d6cf7bad93b9b03842674aa0c0b34b8a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alain=20Cousini=C3=A9?= Date: Thu, 1 Oct 2026 16:31:05 +0200 Subject: [PATCH] libcamera: software_isp: Add R10 and R10_CSI2P monochrome  formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement support for R10 and R10_CSI2P monochrome packed and unpacked formats in the software ISP and shaders. Signed-off-by: Alain Cousinié --- This patch was tested on an HP 14-eu0xxx laptop with the og0va1b camera module (testing on og0ve1b is currently in progress) using qcam.  src/libcamera/shaders/meson.build             |  2 +  src/libcamera/shaders/raw_mono_1x_packed.frag | 50 +++++++++++++++++++  src/libcamera/shaders/raw_mono_unpacked.frag  | 47 +++++++++++++++++  src/libcamera/software_isp/debayer_egl.cpp    | 42 ++++++++++++++++  4 files changed, 141 insertions(+)  create mode 100644 src/libcamera/shaders/raw_mono_1x_packed.frag  create mode 100644 src/libcamera/shaders/raw_mono_unpacked.frag      if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10) &&          bayerFormat.packing == BayerFormat::Packing::None &&          isStandardBayerOrder(bayerFormat.order)) { @@ -166,6 +177,11 @@ int DebayerEGL::initBayerShaders(PixelFormat inputFormat, PixelFormat outputForm      shaderStridePixels_ = inputConfig_.stride;      switch (inputFormat) { +    /* Monochrome cases: No color phase, proper initialization to 0 for R10 and R10_CSI2P */ +    case libcamera::formats::R10: +        firstRed_x_ = 0.0; +        firstRed_y_ = 0.0; +        break;      case libcamera::formats::SBGGR8:      case libcamera::formats::SBGGR10_CSI2P:      case libcamera::formats::SBGGR12_CSI2P: @@ -197,6 +213,25 @@ int DebayerEGL::initBayerShaders(PixelFormat inputFormat, PixelFormat outputForm      /* Shader selection */      switch (inputFormat) { +    case libcamera::formats::R10: { +        /* Raw 10-bit Monochrome Case (Handles both Packed R10_CSI2P and Unpacked R10 sharing the same value) */ +        BayerFormat bayerFormat = BayerFormat::fromPixelFormat(inputFormat); + +        if (bayerFormat.packing == BayerFormat::Packing::None) { +            fragmentShaderData = raw_mono_unpacked_frag; +            vertexShaderData = bayer_unpacked_vert; +            glFormat_ = GL_RG; +            bytesPerPixel_ = 2; +        } else { +            fragmentShaderData = raw_mono_1x_packed_frag; +            vertexShaderData = identity_vert; +            /* Use configured input stride instead of theoretical width */ +            glFormat_ = GL_LUMINANCE; +            bytesPerPixel_ = 1; +            shaderStridePixels_ = inputConfig_.stride; +        } +        break; +    }      case libcamera::formats::SBGGR8:      case libcamera::formats::SGBRG8:      case libcamera::formats::SGRBG8: @@ -516,6 +551,13 @@ eGLImage *DebayerEGL::getCachedInputFrameBuffer(FrameBuffer *input, std::optiona          eglImageInCache_.emplace_back(fd, std::make_unique(glFormat_, inputConfig_.stride / bytesPerPixel_, height_, inputConfig_.stride, GL_TEXTURE0, 0));          eglImageIn = eglImageInCache_.back().second.get(); +        /* Force GL_NEAREST to preserve LSBs of packed monochrome format */ +        BayerFormat bayerFormat = BayerFormat::fromPixelFormat(inputPixelFormat_); +        if (bayerFormat.order == BayerFormat::Order::MONO && bayerFormat.packing == BayerFormat::Packing::CSI2) { +            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +        } +          if (egl_.createInputDMABufTexture2D(*eglImageIn, input->planes()[0].fd.get()) == 0)              return eglImageIn; -- 2.55.0 diff --git a/src/libcamera/shaders/meson.build b/src/libcamera/shaders/meson.build index c409ff9b0..546053051 100644 --- a/src/libcamera/shaders/meson.build +++ b/src/libcamera/shaders/meson.build @@ -7,6 +7,8 @@ shader_files = files([      'bayer_unpacked.frag',      'bayer_unpacked.vert',      'identity.vert', +    'raw_mono_1x_packed.frag', +    'raw_mono_unpacked.frag',  ])  # Generate header from shaders diff --git a/src/libcamera/shaders/raw_mono_1x_packed.frag b/src/libcamera/shaders/raw_mono_1x_packed.frag new file mode 100644 index 000000000..983834cc8 --- /dev/null +++ b/src/libcamera/shaders/raw_mono_1x_packed.frag @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2026, Alain Cousinié + * raw_mono_1x_packed.frag - Fragment shader for packed monochrome formats (R10_CSI2P) + */ + +#ifdef GL_ES +precision highp float; +#endif + +#define BPP        1.25 + +varying vec2 textureOut; + +uniform vec2 tex_size; +uniform sampler2D tex_y; +uniform float gamma; +uniform float contrastExp; + +float apply_contrast(float value) +{ +    if (value < 0.5) +        return 0.5 * pow(value / 0.5, contrastExp); +    else +        return 1.0 - 0.5 * pow((1.0 - value) / 0.5, contrastExp); +} + +void main(void) +{ +    vec2 pixel_coords = floor(textureOut * tex_size); + +    vec2 center_pixel = pixel_coords + vec2(0.5); +    vec2 center_bytes; +    center_bytes.y = center_pixel.y; +    center_bytes.x = floor(BPP * center_pixel.x); + +    center_bytes = center_bytes / tex_size; + +    /* Linear sampling: GPU merges byte transitions. */ +    float C = texture2D(tex_y, center_bytes).r; +    vec3 rgb = vec3(C, C, C); + +    rgb = clamp(rgb, 0.0, 1.0); +    rgb.r = apply_contrast(rgb.r); +    rgb.g = apply_contrast(rgb.g); +    rgb.b = apply_contrast(rgb.b); +    rgb = pow(rgb, vec3(gamma)); + +    gl_FragColor = vec4(rgb, 1.0); +} diff --git a/src/libcamera/shaders/raw_mono_unpacked.frag b/src/libcamera/shaders/raw_mono_unpacked.frag new file mode 100644 index 000000000..f8a0c14ae --- /dev/null +++ b/src/libcamera/shaders/raw_mono_unpacked.frag @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2026, Alain Cousinié + * raw_mono_unpacked.frag - Fragment shader for raw unpacked monochrome formats (R10) + */ + +#extension GL_EXT_texture_rg : enable + +#ifdef GL_ES +precision highp float; +#endif + +varying vec2 textureOut; + +uniform sampler2D tex_y; +uniform float gamma; +uniform float contrastExp; + +float apply_contrast(float value) +{ +    if (value < 0.5) +        return 0.5 * pow(value / 0.5, contrastExp); +    else +        return 1.0 - 0.5 * pow((1.0 - value) / 0.5, contrastExp); +} + +void main(void) +{ +    /* GL_RG format sampling (16 bits): +     * The .r component contains the 8 MSBs, +     * The .g component contains the remaining LSBs. +     */ +    vec4 texel = texture2D(tex_y, textureOut); +    float C = texel.r + texel.g / 256.0; + +    vec3 rgb = vec3(C, C, C); + +    /* Generic rendering processing */ +    rgb = clamp(rgb, 0.0, 1.0); +    rgb.r = apply_contrast(rgb.r); +    rgb.g = apply_contrast(rgb.g); +    rgb.b = apply_contrast(rgb.b); +    rgb = pow(rgb, vec3(gamma)); + +    gl_FragColor = vec4(rgb, 1.0); +} + diff --git a/src/libcamera/software_isp/debayer_egl.cpp b/src/libcamera/software_isp/debayer_egl.cpp index 300822f9d..2a57fb7d4 100644 --- a/src/libcamera/software_isp/debayer_egl.cpp +++ b/src/libcamera/software_isp/debayer_egl.cpp @@ -61,6 +61,17 @@ int DebayerEGL::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf                             formats::XBGR8888,                             formats::ABGR8888 }; +    /* Handle Monochrome case using bayerFormat */ +    if (bayerFormat.order == BayerFormat::Order::MONO) { +        bool isPacked = (bayerFormat.packing == BayerFormat::Packing::CSI2); + +        config.bpp = bayerFormat.bitDepth; /* Dynamic (10 bits for this sensor) */ +        config.patternSize.width = isPacked ? 4 : 1; +        config.patternSize.height = 1; /* No 2x2 pattern in monochrome */ +        config.outputFormats = outputFormats; +        return 0; +    } +