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
b/src/libcamera/shaders/raw_mono_1x_packed.frag
new file mode 100644
@@ -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);
+}
b/src/libcamera/shaders/raw_mono_unpacked.frag
new file mode 100644
@@ -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);
+}
+
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;
+ }
+