@@ -7,6 +7,9 @@ shader_files = files([
'bayer_unpacked.frag',
'bayer_unpacked.vert',
'identity.vert',
+ 'mono_1x_packed.frag',
+ 'mono_unpacked.frag',
+ 'mono.vert',
])
# Generate header from shaders
new file mode 100644
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2026, Alain Cousinié
+ *
+ * Optimized vertex shader for MIPI CSI-2 packed monochrome formats
+ * Pre-calculates exact pixel coordinates to reduce Fragment Shader overhead
+ */
+
+attribute vec4 vertexIn;
+attribute vec2 textureIn;
+
+varying vec2 textureOut;
+varying vec2 v_pixelCoords;
+
+uniform mat4 proj_matrix;
+uniform float stride_factor;
+uniform vec2 tex_size;
+
+void main(void)
+{
+ gl_Position = proj_matrix * vertexIn;
+
+ textureOut = vec2(textureIn.x * stride_factor, textureIn.y);
+ v_pixelCoords = textureOut * tex_size;
+}
new file mode 100644
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2026, Alain Cousinié
+ * Unified Fragment shader for MIPI CSI-2 packed monochrome formats (R10P / R12P)
+ */
+
+#ifdef GL_ES
+#ifdef GL_FRAGMENT_PRECISION_HIGH
+/* High precision prevents float rounding errors during bit-shifting math. */
+precision highp float;
+#else
+precision mediump float;
+#endif
+#endif
+
+varying vec2 textureOut;
+varying vec2 v_pixelCoords;
+
+uniform sampler2D tex_y;
+uniform float gamma;
+uniform float contrastExp;
+uniform vec3 blacklevel;
+
+#define INV_TEX_WIDTH (1.0 / TEX_WIDTH)
+#define INV_TEX_HEIGHT (1.0 / TEX_HEIGHT)
+
+void main(void)
+{
+ float px = floor(v_pixelCoords.x);
+ float py = floor(v_pixelCoords.y);
+
+ /* Compute absolute macro-block start byte and local pixel index. */
+ float blockStart = floor(px / BLOCK_PIXELS) * BLOCK_BYTES;
+ float idx = px - (blockStart * (BLOCK_PIXELS / BLOCK_BYTES));
+
+ /* Target the center of the dedicated MSB texel. */
+ float xMSB = (blockStart + idx + 0.5) * INV_TEX_WIDTH;
+
+ /* Target the shared trailing byte containing the LSBs (1/8bits = 0.125) . */
+ float lsbOffset = BLOCK_PIXELS + floor(idx * LSB_SIZE * 0.125);
+ float xLSB = (blockStart + lsbOffset + 0.5) * INV_TEX_WIDTH;
+ float normY = (py + 0.5) * INV_TEX_HEIGHT;
+
+ float byteHigh = floor(texture2D(tex_y, vec2(xMSB, normY)).r * 255.0 + 0.5);
+ float byteLow = floor(texture2D(tex_y, vec2(xLSB, normY)).r * 255.0 + 0.5);
+
+ /* Isolate the pixel's LSB bits inside the shared byte */
+ float totalBitsPos = idx * LSB_SIZE;
+ float bitStartInByte = totalBitsPos - (floor(totalBitsPos * 0.125) * 8.0);
+
+ float divisionFactor = pow(2.0, bitStartInByte);
+ float shiftedLsb = floor(byteLow / divisionFactor);
+ float lsbBits = shiftedLsb - (floor(shiftedLsb / LSB_MASK_VAL) * LSB_MASK_VAL);
+
+ /* Reconstruct full native intensity value. */
+ float rawValue = (byteHigh * LSB_MASK_VAL) + lsbBits;
+
+ float normalizedIntensity = rawValue * INV_MAX_VAL;
+ normalizedIntensity = clamp(normalizedIntensity - blacklevel.g, 0.0, 1.0);
+
+ /* Apply S-Curve contrast stretching. */
+ float centerDist = 1.0 - abs(2 * normalizedIntensity - 1.0);
+ float singlePow = 0.5 * pow(centerDist, contrastExp);
+ normalizedIntensity = mix(singlePow, 1.0 - singlePow, step(0.5, normalizedIntensity));
+
+ /* Apply gamma correction and output. */
+ vec3 rgb = pow(vec3(normalizedIntensity), vec3(gamma));
+ gl_FragColor = vec4(rgb, 1.0);
+}
new file mode 100644
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2026, Alain Cousinié
+ * Unified Fragment shader for unpacked monochrome formats (R8, R10, R12, R16)
+ */
+
+#ifdef GL_ES
+#ifdef GL_FRAGMENT_PRECISION_HIGH
+/* High precision prevents float rounding errors during bit-shifting math. */
+precision highp float;
+#else
+precision mediump float;
+#endif
+#endif
+
+varying vec2 textureOut;
+
+uniform sampler2D tex_y;
+uniform float gamma;
+uniform float contrastExp;
+uniform vec3 blacklevel;
+
+void main(void)
+{
+ float rawValue = 0.0;
+
+#ifdef RAW8
+ /* 8-bit unpacked: single channel extraction. */
+ rawValue = floor(texture2D(tex_y, textureOut).r * 255.0 + 0.5);
+#else
+ /* Multi-byte unpacked: merge Red (low byte) and Green (high byte). */
+ vec4 texel = texture2D(tex_y, textureOut);
+
+ float byteLow = floor(texel.r * 255.0 + 0.5);
+ float byteHigh = floor(texel.g * 255.0 + 0.5);
+
+ rawValue = (byteHigh * 256.0) + byteLow;
+#endif
+
+ float normalizedIntensity = rawValue * INV_MAX_VAL;
+ normalizedIntensity = clamp(normalizedIntensity - blacklevel.g, 0.0, 1.0);
+
+ /* S-Curve contrast stretching. */
+ float centerDist = 1.0 - abs(2.0 * normalizedIntensity - 1.0);
+ float singlePow = 0.5 * pow(centerDist, contrastExp);
+ normalizedIntensity = mix(singlePow, 1.0 - singlePow, step(0.5, normalizedIntensity));
+
+ /* Gamma correction and output. */
+ vec3 rgb = pow(vec3(normalizedIntensity), vec3(gamma));
+ gl_FragColor = vec4(rgb, 1.0);
+}
Signed-off-by: Alain Cousinié <alain.cousinie@laposte.net> --- src/libcamera/shaders/meson.build | 3 + src/libcamera/shaders/mono.vert | 25 ++++++++ src/libcamera/shaders/mono_1x_packed.frag | 69 +++++++++++++++++++++++ src/libcamera/shaders/mono_unpacked.frag | 51 +++++++++++++++++ 4 files changed, 148 insertions(+) create mode 100644 src/libcamera/shaders/mono.vert create mode 100644 src/libcamera/shaders/mono_1x_packed.frag create mode 100644 src/libcamera/shaders/mono_unpacked.frag -- 2.55.0