[gpu] Return edge coverage from hb_gpu_paint(); apply stem darkening and gamma to it hb_gpu_paint() now takes an out float coverage parameter that receives the maximum slug coverage across all paint layers. This lets the caller apply stem darkening and gamma correction to edge antialiasing only, without distorting interior paint colors (gradients, semi-transparent layers). The demo shaders (GLSL, MSL, HLSL, WGSL) use the new parameter to share a single coverage-correction block between draw and paint paths. Also fixes the blend func to GL_ONE for correct premultiplied compositing. Assisted-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
diff --git a/src/hb-gpu-paint-fragment-glsl.hh b/src/hb-gpu-paint-fragment-glsl.hh index d13fcd5..c6d00ae 100644 --- a/src/hb-gpu-paint-fragment-glsl.hh +++ b/src/hb-gpu-paint-fragment-glsl.hh
@@ -294,7 +294,8 @@ " */\n" "#define HB_GPU_PAINT_GROUP_DEPTH 4\n" "\n" -"vec4 hb_gpu_paint (vec2 renderCoord, uint glyphLoc, vec4 foreground)\n" +"vec4 hb_gpu_paint (vec2 renderCoord, uint glyphLoc, vec4 foreground,\n" +" out float coverage)\n" "{\n" " /* fwidth once, at uniform control flow: every per-layer\n" " * coverage sample below uses this pre-computed pixelsPerEm via\n" @@ -310,6 +311,7 @@ " vec4 acc = vec4 (0.0);\n" " vec4 group_stack[HB_GPU_PAINT_GROUP_DEPTH];\n" " int sp = 0;\n" +" coverage = 0.0;\n" "\n" " for (int i = 0; i < num_ops; i++)\n" " {\n" @@ -334,6 +336,7 @@ " float cov = _hb_gpu_layer_coverage (renderCoord, pixelsPerEm,\n" " base, aux,\n" " payload, clip2_payload, clip3_payload);\n" +" coverage = max (coverage, cov);\n" " vec4 src = vec4 (col.rgb * col.a, col.a) * cov;\n" " acc = src + acc * (1.0 - src.a);\n" "\n" @@ -370,6 +373,7 @@ " float cov = _hb_gpu_layer_coverage (renderCoord, pixelsPerEm,\n" " base, aux,\n" " payload, clip2_payload, clip3_payload);\n" +" coverage = max (coverage, cov);\n" " vec4 src = vec4 (col.rgb * col.a, col.a) * cov;\n" " acc = src + acc * (1.0 - src.a);\n" "\n"
diff --git a/src/hb-gpu-paint-fragment-hlsl.hh b/src/hb-gpu-paint-fragment-hlsl.hh index c11f652..844782f 100644 --- a/src/hb-gpu-paint-fragment-hlsl.hh +++ b/src/hb-gpu-paint-fragment-hlsl.hh
@@ -242,7 +242,8 @@ "\n" "#define HB_GPU_PAINT_GROUP_DEPTH 4\n" "\n" -"float4 hb_gpu_paint (float2 renderCoord, uint glyphLoc_, float4 foreground)\n" +"float4 hb_gpu_paint (float2 renderCoord, uint glyphLoc_, float4 foreground,\n" +" out float coverage)\n" "{\n" " /* fwidth once, at uniform control flow. */\n" " float2 pixelsPerEm = 1.0 / fwidth (renderCoord);\n" @@ -255,6 +256,7 @@ "\n" " float4 acc = float4 (0.0, 0.0, 0.0, 0.0);\n" " float4 group_stack[HB_GPU_PAINT_GROUP_DEPTH];\n" +" coverage = 0.0;\n" " int sp = 0;\n" "\n" " for (int i = 0; i < num_ops; i++)\n" @@ -280,6 +282,7 @@ " float cov = _hb_gpu_layer_coverage (renderCoord, pixelsPerEm,\n" " base, aux,\n" " payload, clip2_payload, clip3_payload);\n" +" coverage = max (coverage, cov);\n" " float4 src = float4 (col.rgb * col.a, col.a) * cov;\n" " acc = src + acc * (1.0 - src.a);\n" "\n" @@ -313,6 +316,7 @@ " float cov = _hb_gpu_layer_coverage (renderCoord, pixelsPerEm,\n" " base, aux,\n" " payload, clip2_payload, clip3_payload);\n" +" coverage = max (coverage, cov);\n" " float4 src = float4 (col.rgb * col.a, col.a) * cov;\n" " acc = src + acc * (1.0 - src.a);\n" "\n"
diff --git a/src/hb-gpu-paint-fragment-msl.hh b/src/hb-gpu-paint-fragment-msl.hh index e57c0ae..dcffcfd 100644 --- a/src/hb-gpu-paint-fragment-msl.hh +++ b/src/hb-gpu-paint-fragment-msl.hh
@@ -263,7 +263,8 @@ "#define HB_GPU_PAINT_GROUP_DEPTH 4\n" "\n" "float4 hb_gpu_paint (float2 renderCoord, uint glyphLoc_, float4 foreground,\n" -" device const short4* hb_gpu_atlas)\n" +" device const short4* hb_gpu_atlas,\n" +" thread float &coverage)\n" "{\n" " /* fwidth once, at uniform control flow. */\n" " float2 pixelsPerEm = 1.0 / fwidth (renderCoord);\n" @@ -277,6 +278,7 @@ " float4 acc = float4 (0.0);\n" " float4 group_stack[HB_GPU_PAINT_GROUP_DEPTH];\n" " int sp = 0;\n" +" coverage = 0.0;\n" "\n" " for (int i = 0; i < num_ops; i++)\n" " {\n" @@ -302,6 +304,7 @@ " base, aux,\n" " payload, clip2_payload, clip3_payload,\n" " hb_gpu_atlas);\n" +" coverage = max (coverage, cov);\n" " float4 src = float4 (col.rgb * col.a, col.a) * cov;\n" " acc = src + acc * (1.0 - src.a);\n" "\n" @@ -339,6 +342,7 @@ " base, aux,\n" " payload, clip2_payload, clip3_payload,\n" " hb_gpu_atlas);\n" +" coverage = max (coverage, cov);\n" " float4 src = float4 (col.rgb * col.a, col.a) * cov;\n" " acc = src + acc * (1.0 - src.a);\n" "\n"
diff --git a/src/hb-gpu-paint-fragment-wgsl.hh b/src/hb-gpu-paint-fragment-wgsl.hh index 2717802..0dd70aa 100644 --- a/src/hb-gpu-paint-fragment-wgsl.hh +++ b/src/hb-gpu-paint-fragment-wgsl.hh
@@ -260,7 +260,8 @@ "const HB_GPU_PAINT_GROUP_DEPTH: i32 = 4;\n" "\n" "fn hb_gpu_paint (renderCoord: vec2f, glyphLoc_: u32, foreground: vec4f,\n" -" hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> vec4f\n" +" hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>,\n" +" coverage: ptr<function, f32>) -> vec4f\n" "{\n" " /* Compute pixelsPerEm once here at uniform control flow. WGSL\n" " * rejects fwidth inside a loop-conditional branch, so we call\n" @@ -279,6 +280,7 @@ " var acc = vec4f (0.0);\n" " var group_stack: array<vec4f, HB_GPU_PAINT_GROUP_DEPTH>;\n" " var sp: i32 = 0;\n" +" *coverage = 0.0;\n" "\n" " for (var i: i32 = 0; i < num_ops; i = i + 1)\n" " {\n" @@ -306,6 +308,7 @@ " base, aux,\n" " payload, clip2_payload, clip3_payload,\n" " hb_gpu_atlas);\n" +" *coverage = max (*coverage, cov);\n" " let src = vec4f (col.rgb * col.a, col.a) * cov;\n" " acc = src + acc * (1.0 - src.a);\n" "\n" @@ -342,6 +345,7 @@ " base, aux,\n" " payload, clip2_payload, clip3_payload,\n" " hb_gpu_atlas);\n" +" *coverage = max (*coverage, cov);\n" " let src = vec4f (col.rgb * col.a, col.a) * cov;\n" " acc = src + acc * (1.0 - src.a);\n" "\n"
diff --git a/src/hb-gpu-paint-fragment.glsl b/src/hb-gpu-paint-fragment.glsl index 9ab7286..37fa00f 100644 --- a/src/hb-gpu-paint-fragment.glsl +++ b/src/hb-gpu-paint-fragment.glsl
@@ -293,7 +293,8 @@ */ #define HB_GPU_PAINT_GROUP_DEPTH 4 -vec4 hb_gpu_paint (vec2 renderCoord, uint glyphLoc, vec4 foreground) +vec4 hb_gpu_paint (vec2 renderCoord, uint glyphLoc, vec4 foreground, + out float coverage) { /* fwidth once, at uniform control flow: every per-layer * coverage sample below uses this pre-computed pixelsPerEm via @@ -309,6 +310,7 @@ vec4 acc = vec4 (0.0); vec4 group_stack[HB_GPU_PAINT_GROUP_DEPTH]; int sp = 0; + coverage = 0.0; for (int i = 0; i < num_ops; i++) { @@ -333,6 +335,7 @@ float cov = _hb_gpu_layer_coverage (renderCoord, pixelsPerEm, base, aux, payload, clip2_payload, clip3_payload); + coverage = max (coverage, cov); vec4 src = vec4 (col.rgb * col.a, col.a) * cov; acc = src + acc * (1.0 - src.a); @@ -369,6 +372,7 @@ float cov = _hb_gpu_layer_coverage (renderCoord, pixelsPerEm, base, aux, payload, clip2_payload, clip3_payload); + coverage = max (coverage, cov); vec4 src = vec4 (col.rgb * col.a, col.a) * cov; acc = src + acc * (1.0 - src.a);
diff --git a/src/hb-gpu-paint-fragment.hlsl b/src/hb-gpu-paint-fragment.hlsl index a82ec27..4a0da60 100644 --- a/src/hb-gpu-paint-fragment.hlsl +++ b/src/hb-gpu-paint-fragment.hlsl
@@ -241,7 +241,8 @@ #define HB_GPU_PAINT_GROUP_DEPTH 4 -float4 hb_gpu_paint (float2 renderCoord, uint glyphLoc_, float4 foreground) +float4 hb_gpu_paint (float2 renderCoord, uint glyphLoc_, float4 foreground, + out float coverage) { /* fwidth once, at uniform control flow. */ float2 pixelsPerEm = 1.0 / fwidth (renderCoord); @@ -254,6 +255,7 @@ float4 acc = float4 (0.0, 0.0, 0.0, 0.0); float4 group_stack[HB_GPU_PAINT_GROUP_DEPTH]; + coverage = 0.0; int sp = 0; for (int i = 0; i < num_ops; i++) @@ -279,6 +281,7 @@ float cov = _hb_gpu_layer_coverage (renderCoord, pixelsPerEm, base, aux, payload, clip2_payload, clip3_payload); + coverage = max (coverage, cov); float4 src = float4 (col.rgb * col.a, col.a) * cov; acc = src + acc * (1.0 - src.a); @@ -312,6 +315,7 @@ float cov = _hb_gpu_layer_coverage (renderCoord, pixelsPerEm, base, aux, payload, clip2_payload, clip3_payload); + coverage = max (coverage, cov); float4 src = float4 (col.rgb * col.a, col.a) * cov; acc = src + acc * (1.0 - src.a);
diff --git a/src/hb-gpu-paint-fragment.msl b/src/hb-gpu-paint-fragment.msl index 624c818..6829d12 100644 --- a/src/hb-gpu-paint-fragment.msl +++ b/src/hb-gpu-paint-fragment.msl
@@ -262,7 +262,8 @@ #define HB_GPU_PAINT_GROUP_DEPTH 4 float4 hb_gpu_paint (float2 renderCoord, uint glyphLoc_, float4 foreground, - device const short4* hb_gpu_atlas) + device const short4* hb_gpu_atlas, + thread float &coverage) { /* fwidth once, at uniform control flow. */ float2 pixelsPerEm = 1.0 / fwidth (renderCoord); @@ -276,6 +277,7 @@ float4 acc = float4 (0.0); float4 group_stack[HB_GPU_PAINT_GROUP_DEPTH]; int sp = 0; + coverage = 0.0; for (int i = 0; i < num_ops; i++) { @@ -301,6 +303,7 @@ base, aux, payload, clip2_payload, clip3_payload, hb_gpu_atlas); + coverage = max (coverage, cov); float4 src = float4 (col.rgb * col.a, col.a) * cov; acc = src + acc * (1.0 - src.a); @@ -338,6 +341,7 @@ base, aux, payload, clip2_payload, clip3_payload, hb_gpu_atlas); + coverage = max (coverage, cov); float4 src = float4 (col.rgb * col.a, col.a) * cov; acc = src + acc * (1.0 - src.a);
diff --git a/src/hb-gpu-paint-fragment.wgsl b/src/hb-gpu-paint-fragment.wgsl index d290ab4..6e462e9 100644 --- a/src/hb-gpu-paint-fragment.wgsl +++ b/src/hb-gpu-paint-fragment.wgsl
@@ -259,7 +259,8 @@ const HB_GPU_PAINT_GROUP_DEPTH: i32 = 4; fn hb_gpu_paint (renderCoord: vec2f, glyphLoc_: u32, foreground: vec4f, - hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> vec4f + hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>, + coverage: ptr<function, f32>) -> vec4f { /* Compute pixelsPerEm once here at uniform control flow. WGSL * rejects fwidth inside a loop-conditional branch, so we call @@ -278,6 +279,7 @@ var acc = vec4f (0.0); var group_stack: array<vec4f, HB_GPU_PAINT_GROUP_DEPTH>; var sp: i32 = 0; + *coverage = 0.0; for (var i: i32 = 0; i < num_ops; i = i + 1) { @@ -305,6 +307,7 @@ base, aux, payload, clip2_payload, clip3_payload, hb_gpu_atlas); + *coverage = max (*coverage, cov); let src = vec4f (col.rgb * col.a, col.a) * cov; acc = src + acc * (1.0 - src.a); @@ -341,6 +344,7 @@ base, aux, payload, clip2_payload, clip3_payload, hb_gpu_atlas); + *coverage = max (*coverage, cov); let src = vec4f (col.rgb * col.a, col.a) * cov; acc = src + acc * (1.0 - src.a);
diff --git a/util/gpu/demo-fragment-glsl.hh b/util/gpu/demo-fragment-glsl.hh index d69e96b..b251bcb 100644 --- a/util/gpu/demo-fragment-glsl.hh +++ b/util/gpu/demo-fragment-glsl.hh
@@ -15,35 +15,31 @@ "\n" "void main ()\n" "{\n" +" float cov;\n" "#ifdef HB_GPU_DEMO_DRAW\n" -" /* Fast path for fonts with no color paint: call the Slug\n" -" * coverage sampler directly and tint by the foreground uniform.\n" -" * Smaller shader program, better occupancy. */\n" -" float cov = hb_gpu_draw (v_texcoord, v_glyphLoc);\n" +" cov = hb_gpu_draw (v_texcoord, v_glyphLoc);\n" " vec4 c = vec4 (u_foreground.rgb * u_foreground.a, u_foreground.a) * cov;\n" "#else\n" -" /* Paint interpreter -- handles monochrome via a synthesized\n" -" * LAYER_SOLID and color paint trees otherwise. */\n" -" vec4 c = hb_gpu_paint (v_texcoord, v_glyphLoc, u_foreground);\n" +" vec4 c = hb_gpu_paint (v_texcoord, v_glyphLoc, u_foreground, cov);\n" "#endif\n" "\n" -" /* Stem darkening: scale the premultiplied value by the\n" -" * alpha-channel darkening ratio so RGB and A stay in lockstep.\n" -" * Brightness is computed from the fragment's own straight color\n" -" * so each paint layer is darkened relative to its own color\n" -" * rather than the foreground uniform (which only matches the\n" -" * synthesized monochrome layer). */\n" -" if (u_stem_darkening > 0.0 && c.a > 0.0)\n" +" /* Apply stem darkening and gamma correction to the edge\n" +" * coverage only, so interior color is unaffected. */\n" +" if (cov > 0.0 && cov < 1.0)\n" " {\n" -" float brightness = dot (c.rgb, vec3 (1.0 / 3.0)) / c.a;\n" -" float darkened = hb_gpu_stem_darken (c.a, brightness,\n" -" 1.0 / max (fwidth (v_texcoord).x, fwidth (v_texcoord).y));\n" -" c *= darkened / c.a;\n" +" float adj = cov;\n" +" if (u_stem_darkening > 0.0)\n" +" {\n" +" float brightness = c.a > 0.0\n" +" ? dot (c.rgb, vec3 (1.0 / 3.0)) / c.a : 0.0;\n" +" adj = hb_gpu_stem_darken (adj, brightness,\n" +" 1.0 / max (fwidth (v_texcoord).x, fwidth (v_texcoord).y));\n" +" }\n" +" if (u_gamma != 1.0)\n" +" adj = pow (adj, u_gamma);\n" +" c *= adj / cov;\n" " }\n" "\n" -" if (u_gamma != 1.0)\n" -" c.a = pow (c.a, u_gamma);\n" -"\n" " if (u_debug > 0.0)\n" " {\n" " ivec2 counts = _hb_gpu_curve_counts (v_texcoord, v_glyphLoc);\n"
diff --git a/util/gpu/demo-fragment.glsl b/util/gpu/demo-fragment.glsl index 77fb934..9873361 100644 --- a/util/gpu/demo-fragment.glsl +++ b/util/gpu/demo-fragment.glsl
@@ -14,35 +14,31 @@ void main () { + float cov; #ifdef HB_GPU_DEMO_DRAW - /* Fast path for fonts with no color paint: call the Slug - * coverage sampler directly and tint by the foreground uniform. - * Smaller shader program, better occupancy. */ - float cov = hb_gpu_draw (v_texcoord, v_glyphLoc); + cov = hb_gpu_draw (v_texcoord, v_glyphLoc); vec4 c = vec4 (u_foreground.rgb * u_foreground.a, u_foreground.a) * cov; #else - /* Paint interpreter -- handles monochrome via a synthesized - * LAYER_SOLID and color paint trees otherwise. */ - vec4 c = hb_gpu_paint (v_texcoord, v_glyphLoc, u_foreground); + vec4 c = hb_gpu_paint (v_texcoord, v_glyphLoc, u_foreground, cov); #endif - /* Stem darkening: scale the premultiplied value by the - * alpha-channel darkening ratio so RGB and A stay in lockstep. - * Brightness is computed from the fragment's own straight color - * so each paint layer is darkened relative to its own color - * rather than the foreground uniform (which only matches the - * synthesized monochrome layer). */ - if (u_stem_darkening > 0.0 && c.a > 0.0) + /* Apply stem darkening and gamma correction to the edge + * coverage only, so interior color is unaffected. */ + if (cov > 0.0 && cov < 1.0) { - float brightness = dot (c.rgb, vec3 (1.0 / 3.0)) / c.a; - float darkened = hb_gpu_stem_darken (c.a, brightness, - 1.0 / max (fwidth (v_texcoord).x, fwidth (v_texcoord).y)); - c *= darkened / c.a; + float adj = cov; + if (u_stem_darkening > 0.0) + { + float brightness = c.a > 0.0 + ? dot (c.rgb, vec3 (1.0 / 3.0)) / c.a : 0.0; + adj = hb_gpu_stem_darken (adj, brightness, + 1.0 / max (fwidth (v_texcoord).x, fwidth (v_texcoord).y)); + } + if (u_gamma != 1.0) + adj = pow (adj, u_gamma); + c *= adj / cov; } - if (u_gamma != 1.0) - c.a = pow (c.a, u_gamma); - if (u_debug > 0.0) { ivec2 counts = _hb_gpu_curve_counts (v_texcoord, v_glyphLoc);
diff --git a/util/gpu/demo-shader-hlsl.hh b/util/gpu/demo-shader-hlsl.hh index 4467f7f..24886de 100644 --- a/util/gpu/demo-shader-hlsl.hh +++ b/util/gpu/demo-shader-hlsl.hh
@@ -44,19 +44,23 @@ " float cov = hb_gpu_draw (input.texcoord, input.glyphLoc);\n" " float4 c = float4 (foreground.rgb * foreground.a, foreground.a) * cov;\n" "#else\n" -" float4 c = hb_gpu_paint (input.texcoord, input.glyphLoc, foreground);\n" -"#endif\n" +" float cov;\n" +" float4 c = hb_gpu_paint (input.texcoord, input.glyphLoc, foreground, cov);\n" "\n" -" if (stem_darkening > 0.0 && c.a > 0.0) {\n" -" float2 fw = fwidth (input.texcoord);\n" -" float ppem = 1.0 / max (fw.x, fw.y);\n" -" float brightness = dot (c.rgb, float3 (1.0/3.0, 1.0/3.0, 1.0/3.0)) / c.a;\n" -" float darkened = hb_gpu_stem_darken (c.a, brightness, ppem);\n" -" c *= darkened / c.a;\n" +" if (cov > 0.0 && cov < 1.0) {\n" +" float adj = cov;\n" +" if (stem_darkening > 0.0) {\n" +" float brightness = c.a > 0.0\n" +" ? dot (c.rgb, float3 (1.0/3.0, 1.0/3.0, 1.0/3.0)) / c.a : 0.0;\n" +" float2 fw = fwidth (input.texcoord);\n" +" adj = hb_gpu_stem_darken (adj, brightness,\n" +" 1.0 / max (fw.x, fw.y));\n" +" }\n" +" if (gamma != 1.0)\n" +" adj = pow (adj, gamma);\n" +" c *= adj / cov;\n" " }\n" -"\n" -" if (gamma != 1.0)\n" -" c.a = pow (c.a, gamma);\n" +"#endif\n" " return c;\n" "}\n" ;
diff --git a/util/gpu/demo-shader-msl.hh b/util/gpu/demo-shader-msl.hh index 63e0b3a..8378320 100644 --- a/util/gpu/demo-shader-msl.hh +++ b/util/gpu/demo-shader-msl.hh
@@ -50,21 +50,24 @@ " float4 c = float4(uniforms.foreground.rgb * uniforms.foreground.a,\n" " uniforms.foreground.a) * cov;\n" "#else\n" -" /* Paint interpreter returns premultiplied RGBA. */\n" +" float cov;\n" " float4 c = hb_gpu_paint(in.texcoord, in.glyphLoc, uniforms.foreground,\n" -" atlas);\n" -"#endif\n" +" atlas, cov);\n" "\n" -" if (uniforms.stem_darkening > 0.0 && c.a > 0.0) {\n" -" float2 fw = fwidth(in.texcoord);\n" -" float ppem = 1.0 / max(fw.x, fw.y);\n" -" float brightness = dot(c.rgb, float3(1.0 / 3.0)) / c.a;\n" -" float darkened = hb_gpu_stem_darken(c.a, brightness, ppem);\n" -" c *= darkened / c.a;\n" +" if (cov > 0.0 && cov < 1.0) {\n" +" float adj = cov;\n" +" if (uniforms.stem_darkening > 0.0) {\n" +" float brightness = c.a > 0.0\n" +" ? dot(c.rgb, float3(1.0 / 3.0)) / c.a : 0.0;\n" +" float2 fw = fwidth(in.texcoord);\n" +" adj = hb_gpu_stem_darken(adj, brightness,\n" +" 1.0 / max(fw.x, fw.y));\n" +" }\n" +" if (uniforms.gamma != 1.0)\n" +" adj = pow(adj, uniforms.gamma);\n" +" c *= adj / cov;\n" " }\n" -"\n" -" if (uniforms.gamma != 1.0)\n" -" c.a = pow(c.a, uniforms.gamma);\n" +"#endif\n" "\n" " if (uniforms.debug > 0.0) {\n" " int2 counts = _hb_gpu_curve_counts(in.texcoord, in.glyphLoc, atlas);\n"
diff --git a/util/gpu/demo-shader-wgsl.hh b/util/gpu/demo-shader-wgsl.hh index ada3dc5..267a5f4 100644 --- a/util/gpu/demo-shader-wgsl.hh +++ b/util/gpu/demo-shader-wgsl.hh
@@ -50,17 +50,21 @@ " let fw = fwidth (in.texcoord);\n" " let ppem = 1.0 / max (fw.x, fw.y);\n" "\n" +" var cov: f32;\n" " var c = hb_gpu_paint (in.texcoord, in.glyphLoc, u.foreground,\n" -" &hb_gpu_atlas);\n" +" &hb_gpu_atlas, &cov);\n" "\n" -" if (u.stem_darkening > 0.0 && c.a > 0.0) {\n" -" let brightness = dot (c.rgb, vec3f (1.0 / 3.0)) / c.a;\n" -" let darkened = hb_gpu_stem_darken (c.a, brightness, ppem);\n" -" c = c * (darkened / c.a);\n" -" }\n" -"\n" -" if (u.gamma != 1.0) {\n" -" c.a = pow (c.a, u.gamma);\n" +" if (cov > 0.0 && cov < 1.0) {\n" +" var adj = cov;\n" +" if (u.stem_darkening > 0.0) {\n" +" let brightness = select (0.0,\n" +" dot (c.rgb, vec3f (1.0 / 3.0)) / c.a, c.a > 0.0);\n" +" adj = hb_gpu_stem_darken (adj, brightness, ppem);\n" +" }\n" +" if (u.gamma != 1.0) {\n" +" adj = pow (adj, u.gamma);\n" +" }\n" +" c = c * (adj / cov);\n" " }\n" "\n" " if (u.debug > 0.0) {\n"
diff --git a/util/gpu/demo-shader.hlsl b/util/gpu/demo-shader.hlsl index d9a69eb..08da0b6 100644 --- a/util/gpu/demo-shader.hlsl +++ b/util/gpu/demo-shader.hlsl
@@ -43,18 +43,22 @@ float cov = hb_gpu_draw (input.texcoord, input.glyphLoc); float4 c = float4 (foreground.rgb * foreground.a, foreground.a) * cov; #else - float4 c = hb_gpu_paint (input.texcoord, input.glyphLoc, foreground); -#endif + float cov; + float4 c = hb_gpu_paint (input.texcoord, input.glyphLoc, foreground, cov); - if (stem_darkening > 0.0 && c.a > 0.0) { - float2 fw = fwidth (input.texcoord); - float ppem = 1.0 / max (fw.x, fw.y); - float brightness = dot (c.rgb, float3 (1.0/3.0, 1.0/3.0, 1.0/3.0)) / c.a; - float darkened = hb_gpu_stem_darken (c.a, brightness, ppem); - c *= darkened / c.a; + if (cov > 0.0 && cov < 1.0) { + float adj = cov; + if (stem_darkening > 0.0) { + float brightness = c.a > 0.0 + ? dot (c.rgb, float3 (1.0/3.0, 1.0/3.0, 1.0/3.0)) / c.a : 0.0; + float2 fw = fwidth (input.texcoord); + adj = hb_gpu_stem_darken (adj, brightness, + 1.0 / max (fw.x, fw.y)); + } + if (gamma != 1.0) + adj = pow (adj, gamma); + c *= adj / cov; } - - if (gamma != 1.0) - c.a = pow (c.a, gamma); +#endif return c; }
diff --git a/util/gpu/demo-shader.msl b/util/gpu/demo-shader.msl index d5e7d72..3db2173 100644 --- a/util/gpu/demo-shader.msl +++ b/util/gpu/demo-shader.msl
@@ -49,21 +49,24 @@ float4 c = float4(uniforms.foreground.rgb * uniforms.foreground.a, uniforms.foreground.a) * cov; #else - /* Paint interpreter returns premultiplied RGBA. */ + float cov; float4 c = hb_gpu_paint(in.texcoord, in.glyphLoc, uniforms.foreground, - atlas); -#endif + atlas, cov); - if (uniforms.stem_darkening > 0.0 && c.a > 0.0) { - float2 fw = fwidth(in.texcoord); - float ppem = 1.0 / max(fw.x, fw.y); - float brightness = dot(c.rgb, float3(1.0 / 3.0)) / c.a; - float darkened = hb_gpu_stem_darken(c.a, brightness, ppem); - c *= darkened / c.a; + if (cov > 0.0 && cov < 1.0) { + float adj = cov; + if (uniforms.stem_darkening > 0.0) { + float brightness = c.a > 0.0 + ? dot(c.rgb, float3(1.0 / 3.0)) / c.a : 0.0; + float2 fw = fwidth(in.texcoord); + adj = hb_gpu_stem_darken(adj, brightness, + 1.0 / max(fw.x, fw.y)); + } + if (uniforms.gamma != 1.0) + adj = pow(adj, uniforms.gamma); + c *= adj / cov; } - - if (uniforms.gamma != 1.0) - c.a = pow(c.a, uniforms.gamma); +#endif if (uniforms.debug > 0.0) { int2 counts = _hb_gpu_curve_counts(in.texcoord, in.glyphLoc, atlas);
diff --git a/util/gpu/demo-shader.wgsl b/util/gpu/demo-shader.wgsl index 8cc96ce..c372e73 100644 --- a/util/gpu/demo-shader.wgsl +++ b/util/gpu/demo-shader.wgsl
@@ -49,17 +49,21 @@ let fw = fwidth (in.texcoord); let ppem = 1.0 / max (fw.x, fw.y); + var cov: f32; var c = hb_gpu_paint (in.texcoord, in.glyphLoc, u.foreground, - &hb_gpu_atlas); + &hb_gpu_atlas, &cov); - if (u.stem_darkening > 0.0 && c.a > 0.0) { - let brightness = dot (c.rgb, vec3f (1.0 / 3.0)) / c.a; - let darkened = hb_gpu_stem_darken (c.a, brightness, ppem); - c = c * (darkened / c.a); - } - - if (u.gamma != 1.0) { - c.a = pow (c.a, u.gamma); + if (cov > 0.0 && cov < 1.0) { + var adj = cov; + if (u.stem_darkening > 0.0) { + let brightness = select (0.0, + dot (c.rgb, vec3f (1.0 / 3.0)) / c.a, c.a > 0.0); + adj = hb_gpu_stem_darken (adj, brightness, ppem); + } + if (u.gamma != 1.0) { + adj = pow (adj, u.gamma); + } + c = c * (adj / cov); } if (u.debug > 0.0) {