Files
external_libcamera/src/libcamera/shaders/bayer_unpacked.frag
T
Bryan O'Donoghue 46aac79a44 libcamera: shaders: Add support for Gamma
Add gamma compensation to the debayer shaders after colour correction is
applied.

Similarly only apply the calculated Gamma curve when using the CCM as the
lookup tables already contain the data.

Suggested-by: Milan Zamazal <mzamazal@redhat.com>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
Tested-by: Robert Mader <robert.mader@collabora.com>
Tested-by: Hans de Goede <johannes.goede@oss.qualcomm.com> # ThinkPad T14s gen 6 (arm64) ov02c10 + X1c gen 12 ov08x40
Tested-by: Kieran Bingham <kieran.bingham@ideasonboard.com> # Lenovo X13s
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2026-01-07 17:02:57 +00:00

168 lines
5.4 KiB
GLSL

/* SPDX-License-Identifier: BSD-2-Clause */
/*
From http://jgt.akpeters.com/papers/McGuire08/
Efficient, High-Quality Bayer Demosaic Filtering on GPUs
Morgan McGuire
This paper appears in issue Volume 13, Number 4.
---------------------------------------------------------
Copyright (c) 2008, Morgan McGuire. All rights reserved.
Modified by Linaro Ltd to integrate it into libcamera.
Copyright (C) 2021, Linaro
*/
//Pixel Shader
#ifdef GL_ES
precision highp float;
#endif
/** Monochrome RGBA or GL_LUMINANCE Bayer encoded texture.*/
uniform sampler2D tex_y;
varying vec4 center;
varying vec4 yCoord;
varying vec4 xCoord;
uniform mat3 ccm;
uniform vec3 blacklevel;
uniform float gamma;
void main(void) {
vec3 rgb;
#define fetch(x, y) texture2D(tex_y, vec2(x, y)).r
float C = texture2D(tex_y, center.xy).r; // ( 0, 0)
const vec4 kC = vec4( 4.0, 6.0, 5.0, 5.0) / 8.0;
// Determine which of four types of pixels we are on.
vec2 alternate = mod(floor(center.zw), 2.0);
vec4 Dvec = vec4(
fetch(xCoord[1], yCoord[1]), // (-1,-1)
fetch(xCoord[1], yCoord[2]), // (-1, 1)
fetch(xCoord[2], yCoord[1]), // ( 1,-1)
fetch(xCoord[2], yCoord[2])); // ( 1, 1)
vec4 PATTERN = (kC.xyz * C).xyzz;
// Can also be a dot product with (1,1,1,1) on hardware where that is
// specially optimized.
// Equivalent to: D = Dvec[0] + Dvec[1] + Dvec[2] + Dvec[3];
Dvec.xy += Dvec.zw;
Dvec.x += Dvec.y;
vec4 value = vec4(
fetch(center.x, yCoord[0]), // ( 0,-2)
fetch(center.x, yCoord[1]), // ( 0,-1)
fetch(xCoord[0], center.y), // (-2, 0)
fetch(xCoord[1], center.y)); // (-1, 0)
vec4 temp = vec4(
fetch(center.x, yCoord[3]), // ( 0, 2)
fetch(center.x, yCoord[2]), // ( 0, 1)
fetch(xCoord[3], center.y), // ( 2, 0)
fetch(xCoord[2], center.y)); // ( 1, 0)
// Even the simplest compilers should be able to constant-fold these to
// avoid the division.
// Note that on scalar processors these constants force computation of some
// identical products twice.
const vec4 kA = vec4(-1.0, -1.5, 0.5, -1.0) / 8.0;
const vec4 kB = vec4( 2.0, 0.0, 0.0, 4.0) / 8.0;
const vec4 kD = vec4( 0.0, 2.0, -1.0, -1.0) / 8.0;
// Conserve constant registers and take advantage of free swizzle on load
#define kE (kA.xywz)
#define kF (kB.xywz)
value += temp;
// There are five filter patterns (identity, cross, checker,
// theta, phi). Precompute the terms from all of them and then
// use swizzles to assign to color channels.
//
// Channel Matches
// x cross (e.g., EE G)
// y checker (e.g., EE B)
// z theta (e.g., EO R)
// w phi (e.g., EO R)
#define A (value[0])
#define B (value[1])
#define D (Dvec.x)
#define E (value[2])
#define F (value[3])
// Avoid zero elements. On a scalar processor this saves two MADDs
// and it has no effect on a vector processor.
PATTERN.yzw += (kD.yz * D).xyy;
PATTERN += (kA.xyz * A).xyzx + (kE.xyw * E).xyxz;
PATTERN.xw += kB.xw * B;
PATTERN.xz += kF.xz * F;
rgb = (alternate.y == 0.0) ?
((alternate.x == 0.0) ?
vec3(C, PATTERN.xy) :
vec3(PATTERN.z, C, PATTERN.w)) :
((alternate.x == 0.0) ?
vec3(PATTERN.w, C, PATTERN.z) :
vec3(PATTERN.yx, C));
rgb = rgb - blacklevel;
/*
* CCM is a 3x3 in the format
*
* +--------------+----------------+---------------+
* | RedRedGain | RedGreenGain | RedBlueGain |
* +--------------+----------------+---------------+
* | GreenRedGain | GreenGreenGain | GreenBlueGain |
* +--------------+----------------+---------------+
* | BlueRedGain | BlueGreenGain | BlueBlueGain |
* +--------------+----------------+---------------+
*
* Rout = RedRedGain * Rin + RedGreenGain * Gin + RedBlueGain * Bin
* Gout = GreenRedGain * Rin + GreenGreenGain * Gin + GreenBlueGain * Bin
* Bout = BlueRedGain * Rin + BlueGreenGain * Gin + BlueBlueGain * Bin
*
* We upload to the GPU without transposition glUniformMatrix3f(.., .., GL_FALSE, ccm);
*
* CPU
* float ccm [] = {
* RedRedGain, RedGreenGain, RedBlueGain,
* GreenRedGain, GreenGreenGain, GreenBlueGain,
* BlueRedGain, BlueGreenGain, BlueBlueGain,
* };
*
* GPU
* ccm = {
* RedRedGain, GreenRedGain, BlueRedGain,
* RedGreenGain, GreenGreenGain, BlueGreenGain,
* RedBlueGain, GreenBlueGain, BlueBlueGain,
* }
*
* However the indexing for the mat data-type is column major hence
* ccm[0][0] = RedRedGain, ccm[0][1] = RedGreenGain, ccm[0][2] = RedBlueGain
*
*/
float rin, gin, bin;
rin = rgb.r;
gin = rgb.g;
bin = rgb.b;
rgb.r = (rin * ccm[0][0]) + (gin * ccm[0][1]) + (bin * ccm[0][2]);
rgb.g = (rin * ccm[1][0]) + (gin * ccm[1][1]) + (bin * ccm[1][2]);
rgb.b = (rin * ccm[2][0]) + (gin * ccm[2][1]) + (bin * ccm[2][2]);
/* Apply gamma after colour correction */
rgb = pow(rgb, vec3(gamma));
#if defined (SWAP_BLUE)
gl_FragColor = vec4(rgb.bgr, 1.0);
#else
gl_FragColor = vec4(rgb, 1.0);
#endif
}