fix(Metal): fix incorrect premultiplication of colors (#5172)

`load_color` was multiplying the alpha channel by itself as well, rather
than just the `rgb` channels.

Also made sure to divide alpha out before applying gamma encoding back
to text color when not using linear blending, which was another source
of error.

Probably fixes the problem observed in #5133 -- I couldn't reproduce the
exact color shift, but did see a similar one with some of my test
colors. This most visibly affects dimmed text since it uses an alpha of
less than 1 for the text color.
This commit is contained in:
Mitchell Hashimoto
2025-01-16 20:44:38 -07:00
committed by GitHub

View File

@ -139,7 +139,7 @@ float4 load_color(
// already have the correct color here and // already have the correct color here and
// can premultiply and return it. // can premultiply and return it.
if (display_p3 && !linear) { if (display_p3 && !linear) {
color *= color.a; color.rgb *= color.a;
return color; return color;
} }
@ -167,7 +167,7 @@ float4 load_color(
} }
// Premultiply our color by its alpha. // Premultiply our color by its alpha.
color *= color.a; color.rgb *= color.a;
return color; return color;
} }
@ -503,12 +503,12 @@ fragment float4 cell_text_fragment(
// If we're not doing linear blending, then we need to // If we're not doing linear blending, then we need to
// re-apply the gamma encoding to our color manually. // re-apply the gamma encoding to our color manually.
// //
// We do it BEFORE premultiplying the alpha because // Since the alpha is premultiplied, we need to divide
// we want to produce the effect of not linearizing // it out before unlinearizing and re-multiply it after.
// it in the first place in order to match the look
// of software that never does this.
if (!uniforms.use_linear_blending) { if (!uniforms.use_linear_blending) {
color.rgb /= color.a;
color = unlinearize(color); color = unlinearize(color);
color.rgb *= color.a;
} }
// Fetch our alpha mask for this pixel. // Fetch our alpha mask for this pixel.