Use premultiplied alpha for emoji rendering (#37370)

localcc created

This improves emoji rendering on windows removing artifacts at the edges
by using premultiplied alpha. A bit more context can be found in #37167

Release Notes:

- N/A

Change summary

crates/gpui/src/platform/windows/color_text_raster.hlsl |  3 +
crates/gpui/src/platform/windows/direct_write.rs        | 18 +++++++++-
2 files changed, 18 insertions(+), 3 deletions(-)

Detailed changes

crates/gpui/src/platform/windows/color_text_raster.hlsl 🔗

@@ -39,5 +39,6 @@ cbuffer GlyphLayerTextureParams : register(b0) {
 float4 emoji_rasterization_fragment(PixelInput input): SV_Target {
     float sample = t_layer.Sample(s_layer, input.texcoord.xy).r;
     float alpha_corrected = apply_contrast_and_gamma_correction(sample, run_color.rgb, grayscale_enhanced_contrast, gamma_ratios);
-    return float4(run_color.rgb, alpha_corrected * run_color.a);
+    float alpha = alpha_corrected * run_color.a;
+    return float4(run_color.rgb * alpha, alpha);
 }

crates/gpui/src/platform/windows/direct_write.rs 🔗

@@ -112,10 +112,10 @@ impl GPUState {
                 RenderTarget: [
                     D3D11_RENDER_TARGET_BLEND_DESC {
                         BlendEnable: true.into(),
-                        SrcBlend: D3D11_BLEND_SRC_ALPHA,
+                        SrcBlend: D3D11_BLEND_ONE,
                         DestBlend: D3D11_BLEND_INV_SRC_ALPHA,
                         BlendOp: D3D11_BLEND_OP_ADD,
-                        SrcBlendAlpha: D3D11_BLEND_SRC_ALPHA,
+                        SrcBlendAlpha: D3D11_BLEND_ONE,
                         DestBlendAlpha: D3D11_BLEND_INV_SRC_ALPHA,
                         BlendOpAlpha: D3D11_BLEND_OP_ADD,
                         RenderTargetWriteMask: D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8,
@@ -1132,6 +1132,20 @@ impl DirectWriteState {
             };
         }
 
+        // Convert from premultiplied to straight alpha
+        for chunk in rasterized.chunks_exact_mut(4) {
+            let b = chunk[0] as f32;
+            let g = chunk[1] as f32;
+            let r = chunk[2] as f32;
+            let a = chunk[3] as f32;
+            if a > 0.0 {
+                let inv_a = 255.0 / a;
+                chunk[0] = (b * inv_a).clamp(0.0, 255.0) as u8;
+                chunk[1] = (g * inv_a).clamp(0.0, 255.0) as u8;
+                chunk[2] = (r * inv_a).clamp(0.0, 255.0) as u8;
+            }
+        }
+
         Ok(rasterized)
     }