From a89db4387d677d66864f0b34f67cd07d4245c536 Mon Sep 17 00:00:00 2001 From: v1p3rrrrr <45924304+v1p3rrrrr@users.noreply.github.com> Date: Tue, 8 Sep 2026 19:14:30 +0300 Subject: [PATCH 1/2] Inpaint Highlight Reconstruction Fix --- .../assets/shaders/Bayer2Float/tofloat.glsl | 43 ++++++++++++------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/app/src/main/assets/shaders/Bayer2Float/tofloat.glsl b/app/src/main/assets/shaders/Bayer2Float/tofloat.glsl index 6d2c8c8c3..80151ec5f 100644 --- a/app/src/main/assets/shaders/Bayer2Float/tofloat.glsl +++ b/app/src/main/assets/shaders/Bayer2Float/tofloat.glsl @@ -1,4 +1,3 @@ - precision highp float; precision highp usampler2D; precision mediump sampler2D; @@ -38,6 +37,10 @@ uniform int MinimalInd; #define HLCLIP 0.987 #import interpolation +bool isBadFloat(float val) { + return (floatBitsToUint(val) & 0x7f800000u) == 0x7f800000u; +} + vec3 hue2rgb(float h) { h = fract(h); h *= 6.0; @@ -60,14 +63,16 @@ uniform vec3 Chrominance; // channel (0=R,1=G,2=B) of the raw photosite p, same anchoring as main() int hlFcol(ivec2 p) { ivec2 ph = ivec2(CfaPattern % 2, CfaPattern / 2); - ivec2 f = (QUAD == 1) ? (((p - ph * 2) / 2) & 1) : ((p - ph) & 1); + ivec2 f = (QUAD == 1) ? (((p - ph * 2 + 4) / 2) & 1) : ((p - ph + 2) & 1); return (f.x + f.y == 1) ? 1 : (f.x == 0 ? 0 : 2); } // normalized white-balanced value of a raw sample, same space as OpposedChroma float hlNorm(uint rv, int c) { - vec3 lvl = vec3(blackLevel.r, (blackLevel.g + blackLevel.b) / 2.0, blackLevel.a); - return max(0.0, (float(rv) / float(whitelevel) - lvl[c]) / (1.0 - lvl[c]) / whitePoint[c]); + float lvlC = (c == 0) ? blackLevel.r : ((c == 1) ? (blackLevel.g + blackLevel.b) * 0.5 : blackLevel.a); + float wpC = (c == 0) ? whitePoint.r : ((c == 1) ? whitePoint.g : whitePoint.b); + float val = float(rv) / max(float(whitelevel), 1.0); + return max(0.0, (val - lvlC) / max(1.0 - lvlC, 1e-4) / max(wpC, 1e-4)); } // opposed-colour estimate for channel c from the 3x3 photosite neighbourhood @@ -87,9 +92,9 @@ float hlRefavg(ivec2 p, int c) { cnt[cc] += 1.0; } } - float m0 = cnt[0] > 0.0 ? pow(sum[0] / cnt[0], 1.0 / 3.0) : 0.0; - float m1 = cnt[1] > 0.0 ? pow(sum[1] / cnt[1], 1.0 / 3.0) : 0.0; - float m2 = cnt[2] > 0.0 ? pow(sum[2] / cnt[2], 1.0 / 3.0) : 0.0; + float m0 = (cnt[0] > 0.0 && sum[0] > 0.0) ? pow(max(sum[0] / cnt[0], 1e-6), 1.0 / 3.0) : 0.0; + float m1 = (cnt[1] > 0.0 && sum[1] > 0.0) ? pow(max(sum[1] / cnt[1], 1e-6), 1.0 / 3.0) : 0.0; + float m2 = (cnt[2] > 0.0 && sum[2] > 0.0) ? pow(max(sum[2] / cnt[2], 1e-6), 1.0 / 3.0) : 0.0; float opp = c == 0 ? 0.5 * (m1 + m2) : (c == 1 ? 0.5 * (m0 + m2) : 0.5 * (m0 + m1)); return opp * opp * opp; } @@ -108,7 +113,7 @@ void main() { #if USEGAIN == 1 vec4 gains = texture(GainMap, vec2(xy)*vec2(RawInvSize)); gains.rgb = vec3(gains.r,(gains.g+gains.b)/2.0,gains.a); - gains.rgb /= dot(gains.rgb,vec3(1.0/3.0)); + gains.rgb /= max(dot(gains.rgb,vec3(1.0/3.0)), 1e-4); #else vec3 gains = vec3(1.0); #endif @@ -116,12 +121,12 @@ void main() { vec3 level = vec3(blackLevel.r,(blackLevel.g+blackLevel.b)/2.0,blackLevel.a); #if RGBLAYOUT == 1 //Output = vec3(texelFetch(InputBuffer, (xy+ivec2(0,0)), 0).rgb)/(float(whitelevel)); - vec3 hlRGB = vec3(texelFetch(InputBuffer, (xy), 0).rgb)/(float(whitelevel)); - hlRGB = (hlRGB - level.rgb)/(vec3(1.0)-level.rgb); + vec3 hlRGB = vec3(texelFetch(InputBuffer, (xy), 0).rgb)/max(float(whitelevel), 1.0); + hlRGB = (hlRGB - level.rgb)/max(vec3(1.0)-level.rgb, vec3(1e-4)); #if HLRECON == 1 { vec3 u = max(hlRGB, vec3(0.0)); - vec3 roots = pow(u, vec3(1.0/3.0)); + vec3 roots = mix(vec3(0.0), pow(max(u, vec3(1e-6)), vec3(1.0/3.0)), step(vec3(1e-6), u)); vec3 opp = vec3(0.5*(roots.g+roots.b), 0.5*(roots.r+roots.b), 0.5*(roots.r+roots.g)); vec3 rec = max(u, opp*opp*opp + Chrominance); hlRGB = mix(u, rec, step(vec3(HLCLIP), u)); @@ -148,13 +153,19 @@ void main() { ci = 2; levelC = level.b; gainC = gains.b; } } - Output = float(texelFetch(InputBuffer, (xy), 0).x)/(float(whitelevel)); - float hlVal = (Output - levelC)/(1.0-levelC)/balance; + Output = float(texelFetch(InputBuffer, (xy), 0).x)/max(float(whitelevel), 1.0); + + balance = max(balance, 1e-4); + float denom = max(1.0 - levelC, 1e-4); + + float hlVal = (Output - levelC) / denom / balance; + #if HLRECON == 1 if (hlVal >= HLCLIP) { - // inpaint the clipped photosite from its opposed colours; the value - // stays scene-referred and may exceed 1.0 instead of clipping - Output = gainC * max(hlVal, hlRefavg(xy, ci) + Chrominance[ci]); + float chromaC = (ci == 0) ? Chrominance.r : ((ci == 1) ? Chrominance.g : Chrominance.b); + float ref = hlRefavg(xy, ci); + float outVal = gainC * max(hlVal, ref + chromaC); + Output = isBadFloat(outVal) ? 1.0 : outVal; } else #endif { From 681334c4aadeebb4a93c16f7e14c81626ea82f8f Mon Sep 17 00:00:00 2001 From: v1p3rrrrr <45924304+v1p3rrrrr@users.noreply.github.com> Date: Wed, 9 Sep 2026 13:29:33 +0300 Subject: [PATCH 2/2] Update tofloat.glsl --- app/src/main/assets/shaders/Bayer2Float/tofloat.glsl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/assets/shaders/Bayer2Float/tofloat.glsl b/app/src/main/assets/shaders/Bayer2Float/tofloat.glsl index 80151ec5f..98786079e 100644 --- a/app/src/main/assets/shaders/Bayer2Float/tofloat.glsl +++ b/app/src/main/assets/shaders/Bayer2Float/tofloat.glsl @@ -164,11 +164,14 @@ void main() { if (hlVal >= HLCLIP) { float chromaC = (ci == 0) ? Chrominance.r : ((ci == 1) ? Chrominance.g : Chrominance.b); float ref = hlRefavg(xy, ci); - float outVal = gainC * max(hlVal, ref + chromaC); + float rec = ref + chromaC; + // Direct Darktable inpainting: replace clipped channel with opposed reconstruction + chrominance offset + float outVal = gainC * (isBadFloat(rec) ? hlVal : max(0.0, rec)); Output = isBadFloat(outVal) ? 1.0 : outVal; } else #endif { + // Original clamp, prevents color fringes in non-clipped areas Output = clamp(gainC * hlVal, 0.0, 1.0); } #endif