From 75d6b71f473166cd412fb5c9e80cbbdd962e225f Mon Sep 17 00:00:00 2001 From: blazeroni Date: Sat, 8 Nov 2025 10:06:01 -0800 Subject: [PATCH] Fixed VividLight blend, tweaked ColorBurn --- internal/gen/shared/template.go | 6 ++-- pkg/image/nrgba/blend_gen.go | 18 ++++++------ pkg/image/rgba/blend_gen.go | 36 ++++++++++++------------ pkg/image/tests/blend/vividlight_test.go | 11 ++++++++ 4 files changed, 41 insertions(+), 30 deletions(-) diff --git a/internal/gen/shared/template.go b/internal/gen/shared/template.go index 01ca96e..3f76fd8 100644 --- a/internal/gen/shared/template.go +++ b/internal/gen/shared/template.go @@ -38,7 +38,7 @@ type BlendTemplate struct { var BlendTemplates = []BlendTemplate{ { Name: "ColorBurn", - Kernel: "if $S == 0 || $D < 255 - $S { $R = 0 } else { $R = 255 - ((255-$D)*255 + $S/2)/$S }", + Kernel: "if $S + $D <= 255 { $R = 0 } else { $R = 255 - ((255 - $D) * 255 + $S/2) / $S }", KernelDocs: "Cr = 1 - (1 - Cd) / Cs", }, { @@ -134,8 +134,8 @@ var BlendTemplates = []BlendTemplate{ Name: "VividLight", Kernel: "switch { " + "case $S == 0 || $S == 255: $R = $S; " + - "case $S < 128: $R = min(255-((255-$D)*255/(2*$S)), 255); " + - "default: $R = min(($D*255)/(255-(2*$S-255)), 255) }", + "case $S < 128: $R = 255 - min(((255 - $D) * 255 + $S) / (2 * $S), 255); " + + "default: $R = min((($D * 255) + (255 - $S)) / (510 - 2 * $S), 255) }", KernelDocs: "if Cs < 0.5 { Cr = 1 - (1 - Cd) / (2 * Cs) } else { Cr = Cd / (2 * (1 - Cs)) }", }, } diff --git a/pkg/image/nrgba/blend_gen.go b/pkg/image/nrgba/blend_gen.go index 37d189e..bbd872d 100644 --- a/pkg/image/nrgba/blend_gen.go +++ b/pkg/image/nrgba/blend_gen.go @@ -44,17 +44,17 @@ func BlendColorBurn(pixIter core.PixelIterator, calc core.PixCalculator[*image.N // Calculate the pure blend color // region BLEND-SPECIFIC LOGIC - if sR == 0 || dR < 255-sR { + if sR+dR <= 255 { oR = 0 } else { oR = 255 - ((255-dR)*255+sR/2)/sR } - if sG == 0 || dG < 255-sG { + if sG+dG <= 255 { oG = 0 } else { oG = 255 - ((255-dG)*255+sG/2)/sG } - if sB == 0 || dB < 255-sB { + if sB+dB <= 255 { oB = 0 } else { oB = 255 - ((255-dB)*255+sB/2)/sB @@ -1752,25 +1752,25 @@ func BlendVividLight(pixIter core.PixelIterator, calc core.PixCalculator[*image. case sR == 0 || sR == 255: oR = sR case sR < 128: - oR = min(255-((255-dR)*255/(2*sR)), 255) + oR = 255 - min(((255-dR)*255+sR)/(2*sR), 255) default: - oR = min((dR*255)/(255-(2*sR-255)), 255) + oR = min(((dR*255)+(255-sR))/(510-2*sR), 255) } switch { case sG == 0 || sG == 255: oG = sG case sG < 128: - oG = min(255-((255-dG)*255/(2*sG)), 255) + oG = 255 - min(((255-dG)*255+sG)/(2*sG), 255) default: - oG = min((dG*255)/(255-(2*sG-255)), 255) + oG = min(((dG*255)+(255-sG))/(510-2*sG), 255) } switch { case sB == 0 || sB == 255: oB = sB case sB < 128: - oB = min(255-((255-dB)*255/(2*sB)), 255) + oB = 255 - min(((255-dB)*255+sB)/(2*sB), 255) default: - oB = min((dB*255)/(255-(2*sB-255)), 255) + oB = min(((dB*255)+(255-sB))/(510-2*sB), 255) } // endregion BLEND-SPECIFIC LOGIC diff --git a/pkg/image/rgba/blend_gen.go b/pkg/image/rgba/blend_gen.go index fba2850..bfda3b1 100644 --- a/pkg/image/rgba/blend_gen.go +++ b/pkg/image/rgba/blend_gen.go @@ -46,17 +46,17 @@ func BlendColorBurn(pixIter core.PixelIterator, calc core.PixCalculator[*image.R var kR, kG, kB uint32 // region BLEND-SPECIFIC KERNEL LOGIC - if sR == 0 || dR < 255-sR { + if sR+dR <= 255 { kR = 0 } else { kR = 255 - ((255-dR)*255+sR/2)/sR } - if sG == 0 || dG < 255-sG { + if sG+dG <= 255 { kG = 0 } else { kG = 255 - ((255-dG)*255+sG/2)/sG } - if sB == 0 || dB < 255-sB { + if sB+dB <= 255 { kB = 0 } else { kB = 255 - ((255-dB)*255+sB/2)/sB @@ -86,17 +86,17 @@ func BlendColorBurn(pixIter core.PixelIterator, calc core.PixCalculator[*image.R var kR, kG, kB uint32 // region BLEND-SPECIFIC KERNEL LOGIC - if sR == 0 || dR < 255-sR { + if sR+dR <= 255 { kR = 0 } else { kR = 255 - ((255-dR)*255+sR/2)/sR } - if sG == 0 || dG < 255-sG { + if sG+dG <= 255 { kG = 0 } else { kG = 255 - ((255-dG)*255+sG/2)/sG } - if sB == 0 || dB < 255-sB { + if sB+dB <= 255 { kB = 0 } else { kB = 255 - ((255-dB)*255+sB/2)/sB @@ -2520,25 +2520,25 @@ func BlendVividLight(pixIter core.PixelIterator, calc core.PixCalculator[*image. case sR == 0 || sR == 255: kR = sR case sR < 128: - kR = min(255-((255-dR)*255/(2*sR)), 255) + kR = 255 - min(((255-dR)*255+sR)/(2*sR), 255) default: - kR = min((dR*255)/(255-(2*sR-255)), 255) + kR = min(((dR*255)+(255-sR))/(510-2*sR), 255) } switch { case sG == 0 || sG == 255: kG = sG case sG < 128: - kG = min(255-((255-dG)*255/(2*sG)), 255) + kG = 255 - min(((255-dG)*255+sG)/(2*sG), 255) default: - kG = min((dG*255)/(255-(2*sG-255)), 255) + kG = min(((dG*255)+(255-sG))/(510-2*sG), 255) } switch { case sB == 0 || sB == 255: kB = sB case sB < 128: - kB = min(255-((255-dB)*255/(2*sB)), 255) + kB = 255 - min(((255-dB)*255+sB)/(2*sB), 255) default: - kB = min((dB*255)/(255-(2*sB-255)), 255) + kB = min(((dB*255)+(255-sB))/(510-2*sB), 255) } out[i] = uint8(kR) out[i+1] = uint8(kG) @@ -2569,25 +2569,25 @@ func BlendVividLight(pixIter core.PixelIterator, calc core.PixCalculator[*image. case sR == 0 || sR == 255: kR = sR case sR < 128: - kR = min(255-((255-dR)*255/(2*sR)), 255) + kR = 255 - min(((255-dR)*255+sR)/(2*sR), 255) default: - kR = min((dR*255)/(255-(2*sR-255)), 255) + kR = min(((dR*255)+(255-sR))/(510-2*sR), 255) } switch { case sG == 0 || sG == 255: kG = sG case sG < 128: - kG = min(255-((255-dG)*255/(2*sG)), 255) + kG = 255 - min(((255-dG)*255+sG)/(2*sG), 255) default: - kG = min((dG*255)/(255-(2*sG-255)), 255) + kG = min(((dG*255)+(255-sG))/(510-2*sG), 255) } switch { case sB == 0 || sB == 255: kB = sB case sB < 128: - kB = min(255-((255-dB)*255/(2*sB)), 255) + kB = 255 - min(((255-dB)*255+sB)/(2*sB), 255) default: - kB = min((dB*255)/(255-(2*sB-255)), 255) + kB = min(((dB*255)+(255-sB))/(510-2*sB), 255) } // endregion BLEND-SPECIFIC KERNEL LOGIC diff --git a/pkg/image/tests/blend/vividlight_test.go b/pkg/image/tests/blend/vividlight_test.go index d41acfb..9c02b45 100644 --- a/pkg/image/tests/blend/vividlight_test.go +++ b/pkg/image/tests/blend/vividlight_test.go @@ -75,6 +75,17 @@ func TestBlendVividLight(t *testing.T) { }, tolerance: 0, }, + { + colors: testColors{ + name: "DarkSrc_Underflow", + dst: c(0x00_00_00_ff), + src: c(0x01_01_01_ff), + }, + compositing: map[op.BlendCompositing]color.NRGBA{ + op.CompositeAll: c(0x00_00_00_ff), + }, + tolerance: 0, + }, { colors: testColors{ name: "LightSrc",