Skip to content

Commit 5d116ce

Browse files
Pixel perfect cached glyphs.
1 parent f6dbef1 commit 5d116ce

34 files changed

Lines changed: 100 additions & 70 deletions

File tree

src/ImageSharp.Drawing/Processing/Processors/Text/RichTextGlyphRenderer.cs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ public RichTextGlyphRenderer(
7474
// Turn of caching. The chances of a hit are near-zero.
7575
this.rasterizationRequired = true;
7676
this.noCache = true;
77-
7877
if (path is IPathInternals internals)
7978
{
8079
this.path = internals;
@@ -142,11 +141,16 @@ protected override void BeginGlyph(in FontRectangle bounds, in GlyphRendererPara
142141
new RectangleF(bounds.Location, new(bounds.Width, bounds.Height)),
143142
this.drawingOptions.Transform);
144143

144+
PointF currentBoundsDelta = currentBounds.Location - ClampToPixel(currentBounds.Location);
145+
PointF subPixelLocation = new(
146+
MathF.Round(currentBoundsDelta.X * AccuracyMultiple) / AccuracyMultiple,
147+
MathF.Round(currentBoundsDelta.Y * AccuracyMultiple) / AccuracyMultiple);
148+
145149
SizeF subPixelSize = new(
146150
MathF.Round(currentBounds.Width * AccuracyMultiple) / AccuracyMultiple,
147151
MathF.Round(currentBounds.Height * AccuracyMultiple) / AccuracyMultiple);
148152

149-
this.currentCacheKey = (parameters, new RectangleF(new(0, 0), subPixelSize));
153+
this.currentCacheKey = (parameters, new RectangleF(subPixelLocation, subPixelSize));
150154
if (this.glyphData.ContainsKey(this.currentCacheKey))
151155
{
152156
// We have already drawn the glyph vectors.
@@ -345,7 +349,32 @@ protected override void EndGlyph()
345349
renderData = this.glyphData[this.currentCacheKey];
346350

347351
// Offset the render location by the delta from the cached glyph and this one.
348-
renderLocation = (Point)(path.Bounds.Location - (PointF)renderData.LocationDelta);
352+
Vector2 previousDelta = renderData.LocationDelta;
353+
Vector2 currentLocation = path.Bounds.Location;
354+
Vector2 currentDelta = path.Bounds.Location - ClampToPixel(path.Bounds.Location);
355+
356+
if (previousDelta.Y > currentDelta.Y)
357+
{
358+
// Move the location down to match the previous location offset.
359+
currentLocation += new Vector2(0, previousDelta.Y - currentDelta.Y);
360+
}
361+
else if (previousDelta.Y < currentDelta.Y)
362+
{
363+
// Move the location up to match the previous location offset.
364+
currentLocation -= new Vector2(0, currentDelta.Y - previousDelta.Y);
365+
}
366+
else if (previousDelta.X > currentDelta.X)
367+
{
368+
// Move the location right to match the previous location offset.
369+
currentLocation += new Vector2(previousDelta.X - currentDelta.X, 0);
370+
}
371+
else if (previousDelta.X < currentDelta.X)
372+
{
373+
// Move the location left to match the previous location offset.
374+
currentLocation -= new Vector2(currentDelta.X - previousDelta.X, 0);
375+
}
376+
377+
renderLocation = ClampToPixel(currentLocation);
349378
}
350379

351380
if (renderData.FillMap != null)
@@ -482,7 +511,8 @@ private Matrix3x2 ComputeTransform(in FontRectangle bounds)
482511

483512
private Buffer2D<float> Render(IPath path)
484513
{
485-
// We need to offset the path now against the equivalent pixel position of [0,0] for rasterization.
514+
// We need to offset the path now by the difference between the clamped location and the
515+
// path location.
486516
IPath offsetPath = path.Translate(-ClampToPixel(path.Bounds.Location));
487517
Size size = Rectangle.Ceiling(offsetPath.Bounds).Size;
488518

Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Loading

0 commit comments

Comments
 (0)