Fix #135229: Soft light blending mode for 8 bit colors is wrong

It appears that 8 bit `blend_color_softlight_byte` call used a wrong
blending routing (overlay), while `blend_color_softlight_float` is
correct. Seems that this was never caught. The correct fomula should be
`dst = 2ab + a^2 * (1 - 2b)`.

Pull Request: https://projects.blender.org/blender/blender/pulls/135382
This commit is contained in:
YimingWu
2025-03-11 05:34:21 +01:00
committed by YimingWu
parent 19e8fe18ff
commit df346a1b6b
3 changed files with 49 additions and 12 deletions

View File

@@ -329,21 +329,19 @@ MINLINE void blend_color_screen_byte(uchar dst[4], const uchar src1[4], const uc
MINLINE void blend_color_softlight_byte(uchar dst[4], const uchar src1[4], const uchar src2[4])
{
const int fac = src2[3];
const float fac = (float)(src2[3]) / 255.0f;
if (fac != 0) {
const int mfac = 255 - fac;
const float mfac = 1.0f - fac;
int i = 3;
while (i--) {
int temp;
if (src1[i] < 127) {
temp = ((2 * ((src2[i] / 2) + 64)) * src1[i]) / 255;
}
else {
temp = 255 - (2 * (255 - ((src2[i] / 2) + 64)) * (255 - src1[i]) / 255);
}
dst[i] = (uchar)((temp * fac + src1[i] * mfac) / 255);
/* Using "Pegtop" formula: dst = (1 - 2b) * a^2 + 2ab where a=bottom and b=top color.
* See https://en.wikipedia.org/wiki/Blend_modes */
const float src1val = (float)(src1[i]) / 255.0f;
const float src2val = (float)(src2[i]) / 255.0f;
float screen = 1.0f - (1.0f - src1val) * (1.0f - src2val);
float soft_light = ((1.0f - src1val) * src2val + screen) * src1val;
dst[i] = round_fl_to_uchar_clamp((src1val * mfac + soft_light * fac) * 255.0f);
}
}
else {

View File

@@ -5,6 +5,7 @@
#include "testing/testing.h"
#include "BLI_math_color.h"
#include "BLI_math_color_blend.h"
TEST(math_color, RGBToHSVRoundtrip)
{
@@ -138,3 +139,41 @@ TEST(math_color, srgb_to_linearrgb_v3_v3)
EXPECT_NEAR(56.2383270264f, linear_color[2], kTolerance);
}
}
TEST(math_color, BlendModeConsistency_SoftLight)
{
float fdst[4];
float fcolora[4] = {0.0f, 0.0f, 0.0f, 1.0f};
float fcolorb[4] = {1.0f, 1.0f, 1.0f, 1.0f};
float fcolorc[4] = {1.0f, 1.0f, 1.0f, 0.5f};
float fcolord[4] = {0.5f, 0.5f, 0.5f, 0.5f};
uchar bdst[4];
uchar bcolora[4] = {0, 0, 0, 255};
uchar bcolorb[4] = {255, 255, 255, 255};
uchar bcolorc[4] = {255, 255, 255, 128};
uchar bcolord[4] = {128, 128, 128, 128};
blend_color_softlight_float(fdst, fcolora, fcolorb);
blend_color_softlight_byte(bdst, bcolora, bcolorb);
EXPECT_NEAR(fdst[0] * 255.0f, bdst[0], 1.0f);
EXPECT_NEAR(fdst[1] * 255.0f, bdst[1], 1.0f);
EXPECT_NEAR(fdst[2] * 255.0f, bdst[2], 1.0f);
blend_color_softlight_float(fdst, fcolorb, fcolora);
blend_color_softlight_byte(bdst, bcolorb, bcolora);
EXPECT_NEAR(fdst[0] * 255.0f, bdst[0], 1.0f);
EXPECT_NEAR(fdst[1] * 255.0f, bdst[1], 1.0f);
EXPECT_NEAR(fdst[2] * 255.0f, bdst[2], 1.0f);
blend_color_softlight_float(fdst, fcolorc, fcolora);
blend_color_softlight_byte(bdst, bcolorc, bcolora);
EXPECT_NEAR(fdst[0] * 255.0f, bdst[0], 1.0f);
EXPECT_NEAR(fdst[1] * 255.0f, bdst[1], 1.0f);
EXPECT_NEAR(fdst[2] * 255.0f, bdst[2], 1.0f);
blend_color_softlight_float(fdst, fcolorc, fcolord);
blend_color_softlight_byte(bdst, bcolorc, bcolord);
EXPECT_NEAR(fdst[0] * 255.0f, bdst[0], 1.0f);
EXPECT_NEAR(fdst[1] * 255.0f, bdst[1], 1.0f);
EXPECT_NEAR(fdst[2] * 255.0f, bdst[2], 1.0f);
}