Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
50 changes: 30 additions & 20 deletions src/SixLabors.Fonts/Tables/TrueType/Hinting/TrueTypeInterpreter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ internal class TrueTypeInterpreter
private const int MaxCallStack = 128;
private const float Epsilon = 0.000001F;

private readonly List<OpCode> debugList = new();
private readonly List<OpCode> debugList = [];

public TrueTypeInterpreter(int maxStack, int maxStorage, int maxFunctions, int maxInstructionDefs, int maxTwilightPoints)
{
Expand All @@ -77,7 +77,7 @@ public TrueTypeInterpreter(int maxStack, int maxStorage, int maxFunctions, int m
this.state = default;
this.cvtState = default;
this.twilight = new Zone(maxTwilightPoints, isTwilight: true);
this.controlValueTable = Array.Empty<float>();
this.controlValueTable = [];
this.contours = Array.Empty<ushort>();
}

Expand All @@ -97,7 +97,6 @@ public void SetControlValueTable(short[]? cvt, float scale, float ppem, byte[]?
this.controlValueTable = new float[cvt.Length];
}

// TODO: How about SIMD here? Will the JIT vectorize this?
for (int i = 0; i < cvt.Length; i++)
{
this.controlValueTable[i] = cvt[i] * scale;
Expand Down Expand Up @@ -227,16 +226,19 @@ private void Execute(StackInstructionStream stream, bool inFunction, bool allowF
// ==== STORAGE MANAGEMENT ====
case OpCode.RS:
{
int loc = CheckIndex(this.stack.Pop(), this.storage.Length);
this.stack.Push(this.storage[loc]);
int loc = this.stack.Pop();
this.stack.Push((uint)loc < (uint)this.storage.Length ? this.storage[loc] : 0);
}

break;
case OpCode.WS:
{
int value = this.stack.Pop();
int loc = CheckIndex(this.stack.Pop(), this.storage.Length);
this.storage[loc] = value;
int loc = this.stack.Pop();
if ((uint)loc < (uint)this.storage.Length)
{
this.storage[loc] = value;
}
}

break;
Expand All @@ -245,16 +247,22 @@ private void Execute(StackInstructionStream stream, bool inFunction, bool allowF
case OpCode.WCVTP:
{
float value = this.stack.PopFloat();
int loc = CheckIndex(this.stack.Pop(), this.controlValueTable.Length);
this.controlValueTable[loc] = value;
int loc = this.stack.Pop();
if ((uint)loc < (uint)this.controlValueTable.Length)
{
this.controlValueTable[loc] = value;
}
}

break;
case OpCode.WCVTF:
{
int value = this.stack.Pop();
int loc = CheckIndex(this.stack.Pop(), this.controlValueTable.Length);
this.controlValueTable[loc] = value * this.scale;
int loc = this.stack.Pop();
if ((uint)loc < (uint)this.controlValueTable.Length)
{
this.controlValueTable[loc] = value * this.scale;
}
}

break;
Expand Down Expand Up @@ -316,7 +324,7 @@ private void Execute(StackInstructionStream stream, bool inFunction, bool allowF
{
int y = this.stack.Pop();
int x = this.stack.Pop();
var vec = Vector2.Normalize(new Vector2(F2Dot14ToFloat(x), F2Dot14ToFloat(y)));
Vector2 vec = Vector2.Normalize(new Vector2(F2Dot14ToFloat(x), F2Dot14ToFloat(y)));
if (opcode == OpCode.SFVFS)
{
this.state.Freedom = vec;
Expand Down Expand Up @@ -1379,8 +1387,10 @@ private void Execute(StackInstructionStream stream, bool inFunction, bool allowF
amount *= 1 << (6 - this.state.DeltaShift);

// update the CVT
CheckIndex(cvtIndex, this.controlValueTable.Length);
this.controlValueTable[cvtIndex] += F26Dot6ToFloat(amount);
if ((uint)cvtIndex < (uint)this.controlValueTable.Length)
{
this.controlValueTable[cvtIndex] += F26Dot6ToFloat(amount);
}
}
}
}
Expand Down Expand Up @@ -1504,14 +1514,14 @@ private void Execute(StackInstructionStream stream, bool inFunction, bool allowF
}
}

private static int CheckIndex(int index, int length)
private float ReadCvt()
{
Guard.MustBeBetweenOrEqualTo(index, 0, length - 1, nameof(index));
return index;
int index = this.stack.Pop();
return (uint)index < (uint)this.controlValueTable.Length
? this.controlValueTable[index]
: 0F;
}

private float ReadCvt() => this.controlValueTable[CheckIndex(this.stack.Pop(), this.controlValueTable.Length)];

private void OnVectorsUpdated()
{
this.fdotp = Vector2.Dot(this.state.Freedom, this.state.Projection);
Expand Down Expand Up @@ -2474,7 +2484,7 @@ public Zone(ControlPoint[] controlPoints, bool isTwilight)
this.IsTwilight = isTwilight;
this.Current = controlPoints;

var original = new ControlPoint[controlPoints.Length];
ControlPoint[] original = new ControlPoint[controlPoints.Length];
controlPoints.AsSpan().CopyTo(original);
this.Original = original;
this.TouchState = new TouchState[controlPoints.Length];
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions tests/Images/ReferenceOutput/Test_Issue_493_Ogham-.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions tests/Images/ReferenceOutput/Test_Issue_493_Runic-.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
63 changes: 63 additions & 0 deletions tests/SixLabors.Fonts.Tests/Issues/Issues_493.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.Fonts.Tests.Issues;

public class Issues_493
{
[Fact]
public void Test_Issue_493_Ogham()
{
const string text =
"""
Ogham
᚛ᚐᚅᚋᚐᚇᚐᚅᚈᚐ᚜
""";

FontCollection fontCollection = new();
string name = fontCollection.Add(TestFonts.NotoSansOghamRegular).Name;

FontFamily mainFontFamily = fontCollection.Get(name);
Font mainFont = mainFontFamily.CreateFont(30, FontStyle.Regular);

TextOptions options = new(mainFont) { HintingMode = HintingMode.Standard };

TextLayoutTestUtilities.TestLayout(text, options);
}

[Fact]
public void Test_Issue_493_Runic()
{
const string text =
"""
Runic
ᚦᛖᚱᛅᛈᛁᛑᛒᚱᚢᚾᚠᛅᚢᛋ
""";

FontCollection fontCollection = new();
string name = fontCollection.Add(TestFonts.NotoSansRunicRegular).Name;

FontFamily mainFontFamily = fontCollection.Get(name);
Font mainFont = mainFontFamily.CreateFont(30, FontStyle.Regular);

TextOptions options = new(mainFont) { HintingMode = HintingMode.Standard };

TextLayoutTestUtilities.TestLayout(text, options);
}

[Fact]
public void Test_Issue_493_MgOpenCanonic()
{
const string text = "the quick brown fox jumps over the lazy dog";

FontCollection fontCollection = new();
string name = fontCollection.Add(TestFonts.NotoSansRunicRegular).Name;

FontFamily mainFontFamily = fontCollection.Get(name);
Font mainFont = mainFontFamily.CreateFont(30, FontStyle.Regular);

TextOptions options = new(mainFont) { HintingMode = HintingMode.Standard };

TextLayoutTestUtilities.TestLayout(text, options);
}
}
6 changes: 6 additions & 0 deletions tests/SixLabors.Fonts.Tests/TestFonts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,12 @@

public static string MyanmarSansRegular => GetFullPath("NotoSansMyanmar-Regular.ttf");

public static string NotoSansOghamRegular => GetFullPath("NotoSansOgham-Regular.ttf");

public static string NotoSansRunicRegular => GetFullPath("NotoSansRunic-Regular.ttf");

public static string MgOpenCanonicRegular => GetFullPath("mgopencanonicaregular.ttf");

public static Stream TwemojiMozillaData() => OpenStream(TwemojiMozillaFile);

public static Stream SegoeuiEmojiData() => OpenStream(SegoeuiEmojiFile);
Expand Down Expand Up @@ -356,7 +362,7 @@

public static string GetFullPath(string path)
{
string root = Path.GetDirectoryName(new Uri(typeof(TestFonts).GetTypeInfo().Assembly.CodeBase).LocalPath);

Check warning on line 365 in tests/SixLabors.Fonts.Tests/TestFonts.cs

View workflow job for this annotation

GitHub Actions / Build (false, ubuntu-latest, net8.0, 8.0.x, -x64, false)

'Assembly.CodeBase' is obsolete: 'Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location.' (https://aka.ms/dotnet-warnings/SYSLIB0012)

Check warning on line 365 in tests/SixLabors.Fonts.Tests/TestFonts.cs

View workflow job for this annotation

GitHub Actions / Build (false, windows-latest, net8.0, 8.0.x, -x64, true)

'Assembly.CodeBase' is obsolete: 'Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location.' (https://aka.ms/dotnet-warnings/SYSLIB0012)

Check warning on line 365 in tests/SixLabors.Fonts.Tests/TestFonts.cs

View workflow job for this annotation

GitHub Actions / Build (false, windows-latest, net8.0, 8.0.x, -x64, true)

'Assembly.CodeBase' is obsolete: 'Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location.' (https://aka.ms/dotnet-warnings/SYSLIB0012)

Check warning on line 365 in tests/SixLabors.Fonts.Tests/TestFonts.cs

View workflow job for this annotation

GitHub Actions / Build (false, windows-latest, net8.0, 8.0.x, -x64, true)

'Assembly.CodeBase' is obsolete: 'Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location.' (https://aka.ms/dotnet-warnings/SYSLIB0012)

Check warning on line 365 in tests/SixLabors.Fonts.Tests/TestFonts.cs

View workflow job for this annotation

GitHub Actions / Build (false, windows-latest, net8.0, 8.0.x, -x64, true)

'Assembly.CodeBase' is obsolete: 'Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location.' (https://aka.ms/dotnet-warnings/SYSLIB0012)

Check warning on line 365 in tests/SixLabors.Fonts.Tests/TestFonts.cs

View workflow job for this annotation

GitHub Actions / Build (false, windows-latest, net8.0, 8.0.x, -x64, true)

'Assembly.CodeBase' is obsolete: 'Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location.' (https://aka.ms/dotnet-warnings/SYSLIB0012)

string[] paths = new[]
{
Expand Down
Loading