Skip to content

Fix cached color glyph paint replay and add a Paint overload with a text cache - #422

Merged
JimBobSquarePants merged 2 commits into
release/3.1.xfrom
js/emoji-replay-color
Sep 15, 2026
Merged

JimBobSquarePants merged 2 commits into
release/3.1.xfrom
js/emoji-replay-color

Conversation

@JimBobSquarePants

Copy link
Copy Markdown
Member

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following matches the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

Fixes the wrong color of cached color emoji glyphs when a shared DrawingTextCache replays them at a new position, and adds a Paint overload that takes a DrawingTextCache.

Bug. WebGPUWindowDemo scrolls a text block through a shared cache. The emoji in the block rendered flat brown except near one screen position. The same output appears on the CPU backend, so the cause is in the text renderer, not the GPU.

Cause. Layered color glyphs re-create their paint brushes on replay. RichTextGlyphRenderer stored the device-space glyph origin in the cache and appended the device-space delta after the drawing transform. When DrawText(TextBlock, point) passes its origin through DrawingOptions.Transform, that offset was applied twice. The radial gradient moved off the glyph and the pad color filled the face.

Fix. The cache now stores the glyph origin before the drawing transform, and the replay applies the delta before the drawing transform. The replayed brush then matches a fresh build at the new position exactly.

API. Paint(IImageProcessingContext, DrawingOptions, DrawingTextCache, CanvasAction) passes an application-owned cache to every frame canvas. PaintProcessor gets a matching constructor and a TextCache property. This mirrors the CreateCanvas overloads and lets the Image.Mutate path share a cache.

Tests.

  • DrawingTextCacheTests.LayeredGlyph_ReplayAtNewPositionMatchesFreshBuild draws a color emoji through a shared cache at one point, then at a second point, and compares exactly against a fresh-cache draw at the second point. It fails on the old code and passes with the fix.
  • ProcessWithCanvas.CanvasActionWithOptionsAndTextCache checks that the processor receives the cache.
Run Result
Cache, paint and text tests, net10.0 180 pass
WebGPU and issue tests, net10.0 473 pass
All of the above, net8.0 608 pass

The Paint image processing extension created a private text cache for
each frame canvas. The new overload passes an application-owned cache to
every frame canvas so glyph geometry is shared across draws, matching
the CreateCanvas overloads.
Cached color glyph layers re-create their paint brushes on replay. The
cache stored the device-space glyph origin and appended the device-space
delta after the drawing transform, so a draw whose transform carries an
origin applied that offset twice and the gradient drifted off the glyph.
Store the glyph origin before the drawing transform and apply the delta
before it.
@JimBobSquarePants JimBobSquarePants added bug Something isn't working text labels Sep 15, 2026
@codecov

codecov Bot commented Sep 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80%. Comparing base (ef8c9c8) to head (f30b3ba).

Additional details and impacted files
@@              Coverage Diff              @@
##           release/3.1.x    #422   +/-   ##
=============================================
- Coverage             80%     80%   -1%     
=============================================
  Files                232     232           
  Lines              27392   27407   +15     
  Branches            3120    3121    +1     
=============================================
+ Hits               21969   21980   +11     
  Misses              4535    4535           
- Partials             888     892    +4     
Flag Coverage Δ
unittests 80% <100%> (-1%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@JimBobSquarePants
JimBobSquarePants merged commit 22d08fc into release/3.1.x Sep 15, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working text

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant