Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix DFKai-SB and KaiU font rendering #340

Merged
merged 5 commits into from
Jun 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/SixLabors.Fonts/Bounds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// Licensed under the Apache License, Version 2.0.

using System;
using System.Collections.Generic;
using System.Numerics;
using SixLabors.Fonts.Tables.TrueType.Glyphs;

namespace SixLabors.Fonts
{
Expand Down Expand Up @@ -41,6 +43,45 @@ public static Bounds Load(BigEndianBinaryReader reader)
return new Bounds(minX, minY, maxX, maxY);
}

public static Bounds Load(IList<ControlPoint> controlPoints)
{
if (controlPoints is null || controlPoints.Count == 0)
{
return Empty;
}

float xMin = float.MaxValue;
float yMin = float.MaxValue;
float xMax = float.MinValue;
float yMax = float.MinValue;

for (int i = 0; i < controlPoints.Count; i++)
{
Vector2 p = controlPoints[i].Point;
if (p.X < xMin)
{
xMin = p.X;
}

if (p.X > xMax)
{
xMax = p.X;
}

if (p.Y < yMin)
{
yMin = p.Y;
}

if (p.Y > yMax)
{
yMax = p.Y;
}
}

return new Bounds(xMin, yMin, xMax, yMax);
}

public static Bounds Transform(in Bounds bounds, Matrix3x2 matrix)
=> new(Vector2.Transform(bounds.Min, matrix), Vector2.Transform(bounds.Max, matrix));

Expand Down
4 changes: 2 additions & 2 deletions src/SixLabors.Fonts/StreamFontMetrics.TrueType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ internal void ApplyTrueTypeHinting(HintingMode hintingMode, GlyphMetrics metrics
float scaleFactor = pixelSize / this.UnitsPerEm;
this.interpreter.SetControlValueTable(cvt?.ControlValues, scaleFactor, pixelSize, prep?.Instructions);

Bounds bounds = glyphVector.GetBounds();
Bounds bounds = glyphVector.Bounds;

Vector2 pp1 = new(MathF.Round(bounds.Min.X - (metrics.LeftSideBearing * scaleXY.X)), 0);
Vector2 pp2 = new(MathF.Round(pp1.X + (metrics.AdvanceWidth * scaleXY.X)), 0);
Expand Down Expand Up @@ -131,7 +131,7 @@ private GlyphMetrics CreateTrueTypeGlyphMetrics(
VerticalMetricsTable? vtmx = tables.Vmtx;

GlyphVector vector = glyf.GetGlyph(glyphId);
Bounds bounds = vector.GetBounds();
Bounds bounds = vector.Bounds;
ushort advanceWidth = htmx.GetAdvancedWidth(glyphId);
short lsb = htmx.GetLeftSideBearing(glyphId);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Numerics;
using SixLabors.Fonts.Tables.TrueType;
using SixLabors.Fonts.Tables.TrueType.Glyphs;

namespace SixLabors.Fonts.Tables.AdvancedTypographic.GPos
{
Expand Down Expand Up @@ -127,10 +127,10 @@ public override AnchorXY GetAnchor(FontMetrics fontMetrics, GlyphShapingData dat
break;
}

ReadOnlyMemory<Vector2> points = ttmetric.GetOutline().ControlPoints;
if (this.anchorPointIndex < points.Length)
IList<ControlPoint> points = ttmetric.GetOutline().ControlPoints;
if (this.anchorPointIndex < points.Count)
{
Vector2 point = points.Span[this.anchorPointIndex];
Vector2 point = points[this.anchorPointIndex].Point;
return new((short)point.X, (short)point.Y);
}
}
Expand Down
43 changes: 27 additions & 16 deletions src/SixLabors.Fonts/Tables/TrueType/Glyphs/CompositeGlyphLoader.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
Expand All @@ -10,33 +11,40 @@ namespace SixLabors.Fonts.Tables.TrueType.Glyphs
internal sealed class CompositeGlyphLoader : GlyphLoader
{
private readonly Bounds bounds;
private readonly Composite[] result;
private readonly Composite[] composites;
private readonly ReadOnlyMemory<byte> instructions;

public CompositeGlyphLoader(IEnumerable<Composite> result, Bounds bounds)
public CompositeGlyphLoader(IEnumerable<Composite> composites, Bounds bounds, ReadOnlyMemory<byte> instructions)
{
this.result = result.ToArray();
this.composites = composites.ToArray();
this.bounds = bounds;
this.instructions = instructions;
}

public override GlyphVector CreateGlyph(GlyphTable table)
{
GlyphVector glyph = default;
for (int resultIndex = 0; resultIndex < this.result.Length; resultIndex++)
List<ControlPoint> controlPoints = new();
List<ushort> endPoints = new();
for (int i = 0; i < this.composites.Length; i++)
{
ref Composite composite = ref this.result[resultIndex];
Composite composite = this.composites[i];
var clone = GlyphVector.DeepClone(table.GetGlyph(composite.GlyphIndex));
GlyphVector.TransformInPlace(ref clone, composite.Transformation);
glyph = GlyphVector.Append(glyph, clone, this.bounds);
ushort endPointOffset = (ushort)controlPoints.Count;

controlPoints.AddRange(clone.ControlPoints);
foreach (ushort p in clone.EndPoints)
{
endPoints.Add((ushort)(p + endPointOffset));
}
}

// We ignore any composite glyph instructions and
// instead rely on the individual glyph instructions.
return glyph;
return new(controlPoints, endPoints, this.bounds, this.instructions, true);
}

public static CompositeGlyphLoader LoadCompositeGlyph(BigEndianBinaryReader reader, in Bounds bounds)
{
var result = new List<Composite>();
List<Composite> composites = new();
CompositeGlyphFlags flags;
do
{
Expand Down Expand Up @@ -67,19 +75,19 @@ public static CompositeGlyphLoader LoadCompositeGlyph(BigEndianBinaryReader read
transform.M22 = reader.ReadF2dot14();
}

result.Add(new Composite(glyphIndex, transform));
composites.Add(new Composite(glyphIndex, flags, transform));
}
while ((flags & CompositeGlyphFlags.MoreComponents) != 0);

byte[] instructions = Array.Empty<byte>();
if ((flags & CompositeGlyphFlags.WeHaveInstructions) != 0)
{
// Read the instructions if they exist.
// We don't actually use them though and rely on individual glyph instructions.
ushort instructionSize = reader.ReadUInt16();
reader.ReadUInt8Array(instructionSize);
instructions = reader.ReadUInt8Array(instructionSize);
}

return new CompositeGlyphLoader(result, bounds);
return new CompositeGlyphLoader(composites, bounds, instructions);
}

public static void LoadArguments(BigEndianBinaryReader reader, CompositeGlyphFlags flags, out int dx, out int dy)
Expand Down Expand Up @@ -123,14 +131,17 @@ public static void LoadArguments(BigEndianBinaryReader reader, CompositeGlyphFla

public readonly struct Composite
{
public Composite(ushort glyphIndex, Matrix3x2 transformation)
public Composite(ushort glyphIndex, CompositeGlyphFlags flags, Matrix3x2 transformation)
{
this.GlyphIndex = glyphIndex;
this.Flags = flags;
this.Transformation = transformation;
}

public ushort GlyphIndex { get; }

public CompositeGlyphFlags Flags { get; }

public Matrix3x2 Transformation { get; }
}
}
Expand Down
58 changes: 58 additions & 0 deletions src/SixLabors.Fonts/Tables/TrueType/Glyphs/ControlPoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.Numerics;

namespace SixLabors.Fonts.Tables.TrueType.Glyphs
{
/// <summary>
/// Represents a true type glyph control point.
/// </summary>
internal struct ControlPoint : IEquatable<ControlPoint>
{
/// <summary>
/// Gets or sets the position of the point.
/// </summary>
public Vector2 Point;

/// <summary>
/// Gets or sets a value indicating whether the point is on a curve.
/// </summary>
public bool OnCurve;

/// <summary>
/// Initializes a new instance of the <see cref="ControlPoint"/> struct.
/// </summary>
/// <param name="point">The position.</param>
/// <param name="onCurve">Whether the point is on a curve.</param>
public ControlPoint(Vector2 point, bool onCurve)
{
this.Point = point;
this.OnCurve = onCurve;
}

public static bool operator ==(ControlPoint left, ControlPoint right)
=> left.Equals(right);

public static bool operator !=(ControlPoint left, ControlPoint right)
=> !(left == right);

/// <inheritdoc/>
public override bool Equals(object? obj)
=> obj is ControlPoint point && this.Equals(point);

/// <inheritdoc/>
public bool Equals(ControlPoint other)
=> this.Point.Equals(other.Point)
&& this.OnCurve == other.OnCurve;

/// <inheritdoc/>
public override int GetHashCode()
=> HashCode.Combine(this.Point, this.OnCurve);

/// <inheritdoc/>
public override string ToString()
=> FormattableString.Invariant($"Point: {this.Point}, OnCurve: {this.OnCurve}");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public override GlyphVector CreateGlyph(GlyphTable table)
}

this.loop = true;
this.glyph ??= GlyphVector.Empty(table.GetGlyph(0).GetBounds());
this.glyph ??= GlyphVector.Empty(table.GetGlyph(0).Bounds);
return this.glyph.Value;
}
}
Expand Down
45 changes: 0 additions & 45 deletions src/SixLabors.Fonts/Tables/TrueType/Glyphs/GlyphOutline.cs

This file was deleted.

Loading