From 6b8b3f2f0dc93cbd27a9912089494f0c96be1f4c Mon Sep 17 00:00:00 2001 From: "H. Utku Maden" Date: Sun, 16 Jul 2023 18:31:51 +0300 Subject: [PATCH] Create a super basic test for fonts. --- Quik.Media.Stb/Quik.Media.Stb.csproj | 5 - Quik.StbTrueType/Quik.StbTrueType.csproj | 7 + Quik.StbTrueType/StbFont.cs | 355 ++++++++++++++++++ Quik.StbTrueType/Stbtt.Manual.cs | 63 ++++ Quik.sln | 34 +- tests/Quik.Stb.Tests/LoadFont.cs | 72 ++++ .../LoadImage.cs | 14 +- .../Quik.Stb.Tests.csproj} | 1 + tests/Quik.Stb.Tests/res/Varicka.ttf | Bin 0 -> 40624 bytes tests/Quik.Stb.Tests/res/Varicka.txt | 12 + .../res/kodim/LICENSE.md | 0 .../res/kodim/kodim23.gif | Bin .../res/kodim/kodim23.jpg | Bin .../res/kodim/kodim23.png | Bin .../res/kodim/kodim23.tiff | Bin .../res/kodim/kodim23.webp | Bin 16 files changed, 532 insertions(+), 31 deletions(-) create mode 100644 Quik.StbTrueType/StbFont.cs create mode 100644 Quik.StbTrueType/Stbtt.Manual.cs create mode 100644 tests/Quik.Stb.Tests/LoadFont.cs rename tests/{Quik.StbImage.Tests => Quik.Stb.Tests}/LoadImage.cs (70%) rename tests/{Quik.StbImage.Tests/Quik.StbImage.Tests.csproj => Quik.Stb.Tests/Quik.Stb.Tests.csproj} (90%) create mode 100644 tests/Quik.Stb.Tests/res/Varicka.ttf create mode 100644 tests/Quik.Stb.Tests/res/Varicka.txt rename tests/{Quik.StbImage.Tests => Quik.Stb.Tests}/res/kodim/LICENSE.md (100%) rename tests/{Quik.StbImage.Tests => Quik.Stb.Tests}/res/kodim/kodim23.gif (100%) rename tests/{Quik.StbImage.Tests => Quik.Stb.Tests}/res/kodim/kodim23.jpg (100%) rename tests/{Quik.StbImage.Tests => Quik.Stb.Tests}/res/kodim/kodim23.png (100%) rename tests/{Quik.StbImage.Tests => Quik.Stb.Tests}/res/kodim/kodim23.tiff (100%) rename tests/{Quik.StbImage.Tests => Quik.Stb.Tests}/res/kodim/kodim23.webp (100%) diff --git a/Quik.Media.Stb/Quik.Media.Stb.csproj b/Quik.Media.Stb/Quik.Media.Stb.csproj index b91e828..5f5fbf8 100644 --- a/Quik.Media.Stb/Quik.Media.Stb.csproj +++ b/Quik.Media.Stb/Quik.Media.Stb.csproj @@ -7,11 +7,6 @@ True - - - - - diff --git a/Quik.StbTrueType/Quik.StbTrueType.csproj b/Quik.StbTrueType/Quik.StbTrueType.csproj index 108dd89..db979ca 100644 --- a/Quik.StbTrueType/Quik.StbTrueType.csproj +++ b/Quik.StbTrueType/Quik.StbTrueType.csproj @@ -11,4 +11,11 @@ + + + runtimes + true + PreserveNewest + + diff --git a/Quik.StbTrueType/StbFont.cs b/Quik.StbTrueType/StbFont.cs new file mode 100644 index 0000000..cb3cde9 --- /dev/null +++ b/Quik.StbTrueType/StbFont.cs @@ -0,0 +1,355 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace Quik.Stb +{ + public unsafe class StbFont : IDisposable + { + IntPtr _buffer; + stbtt_fontinfo* _info; + List _kerningTable; + + public IntPtr FontBuffer => _buffer; + public ref stbtt_fontinfo FontInfo => ref *_info; + + public IReadOnlyList KerningTable + { + get + { + if (_kerningTable != null) + return _kerningTable; + + int count = Stbtt.GetKerningTableLength(_info); + + if (count == 0) + { + return _kerningTable = new List(); + } + else + { + stbtt_kerningentry[] array = new stbtt_kerningentry[count]; + + fixed (stbtt_kerningentry *ptr = array) + Stbtt.GetKerningTable(_info, ptr, count); + + return _kerningTable = new List(array); + } + } + } + + public int Ascend { get; } + public int Descend { get; } + public int VerticalLineGap { get; } + public int AscendOS2 { get; } + public int DescendOS2 { get; } + public int VerticalLineGapOS2 { get; } + public Box BoundingBox { get; } + + private StbFont(IntPtr buffer, stbtt_fontinfo* info) + { + _buffer = buffer; + _info = info; + + int a, b, c, d; + + Stbtt.GetFontVMetrics(_info, &a, &b, &c); + Ascend = a; + Descend = b; + VerticalLineGap = c; + + Stbtt.GetFontVMetricsOS2(_info, &a, &b, &c); + AscendOS2 = a; + DescendOS2 = b; + VerticalLineGapOS2 = c; + + Stbtt.GetFontBoundingBox(_info, &a, &b, &c, &d); + BoundingBox = new Box(a, b, c, d); + } + ~StbFont() + { + Dispose(false); + } + + + public int FindGlyphIndex(int codepoint) + { + return Stbtt.FindGlyphIndex(_info, codepoint); + } + public int FindGlyphIndex(Rune codepoint) => FindGlyphIndex(codepoint.Value); + + public float ScaleForPixelHeight(float pixels) + { + return Stbtt.ScaleForPixelHeight(_info, pixels); + } + + public float ScaleForMappingEmToPixels(float pixels) + { + return Stbtt.ScaleForMappingEmToPixels(_info, pixels); + } + + public void GetCodepointHMetrics(int codepoint, out int advance, out int bearing) + { + int a, b; + + Stbtt.GetCodepointHMetrics(_info, codepoint, &a, &b); + + advance = a; + bearing = b; + } + + public void GetCodepointHMetrics(Rune codepoint, out int advance, out int bearing) + => GetCodepointHMetrics(codepoint.Value, out advance, out bearing); + + public int GetCodepointKernAdvance(int cp1, int cp2) + { + return Stbtt.GetCodepointKernAdvance(_info, cp1, cp2); + } + public int GetCodepointKernAdvance(Rune cp1, Rune cp2) => GetCodepointKernAdvance(cp1.Value, cp2.Value); + + public int GetCodepointBox(int codepoint, out Box box) + { + int x0, y0; + int x1, y1; + int rval; + + rval = Stbtt.GetCodepointBox(_info, codepoint, &x0, &y0, &x1, &y1); + + box = new Box(x0, y0, x1, y1); + + return rval; + } + + public void GetGlyphHMetrics(int glyph, out int advance, out int bearing) + { + int a, b; + + Stbtt.GetGlyphHMetrics(_info, glyph, &a, &b); + + advance = a; + bearing = b; + } + + public int GetGlyphKernAdvance(int gl1, int gl2) + { + return Stbtt.GetGlyphKernAdvance(_info, gl1, gl2); + } + + public int GetGlyphBox(int glyph, out Box box) + { + int x0, y0; + int x1, y1; + int rval; + + rval = Stbtt.GetGlyphBox(_info, glyph, &x0, &y0, &x1, &y1); + + box = new Box(x0, y0, x1, y1); + + return rval; + } + + public bool IsGlyphEmpty(int glyph) + { + return Stbtt.IsGlyphEmpty(_info, glyph) != 0; + } + + public Bitmap GetCodepointBitmap(float scaleX, float scaleY, int codepoint, out int offsetX, out int offsetY) + { + int w, h, x, y; + void* ptr = Stbtt.GetCodepointBitmap(_info, scaleX, scaleY, codepoint, &w, &h, &x, &y); + + offsetX = x; + offsetY = y; + + return new Bitmap((IntPtr)ptr, w, h, FreeBitmap); + } + + public Bitmap GetCodepointBitmap(float scaleX, float scaleY, Rune codepoint, out int offsetX, out int offsetY) + => GetCodepointBitmap(scaleX, scaleY, codepoint.Value, out offsetX, out offsetY); + + public Bitmap GetCodepointBitmapSubpixel(float scaleX, float scaleY, float shiftX, float shiftY, int codepoint, out int offsetX, out int offsetY) + { + int w, h, x, y; + void* ptr = Stbtt.GetCodepointBitmapSubpixel(_info, scaleX, scaleY, shiftX, shiftY, codepoint, &w, &h, &x, &y); + + offsetX = x; + offsetY = y; + + return new Bitmap((IntPtr)ptr, w, h, FreeBitmap); + } + + public Bitmap GetCodepointBitmapSubpixel(float scaleX, float scaleY, float shiftX, float shiftY, Rune codepoint, out int offsetX, out int offsetY) + => GetCodepointBitmapSubpixel(scaleX, scaleY, shiftX, shiftY, codepoint.Value, out offsetX, out offsetY); + + public Bitmap GetGlyphBitmap(float scaleX, float scaleY, int glyph, out int offsetX, out int offsetY) + { + int w, h, x, y; + void* ptr = Stbtt.GetGlyphBitmap(_info, scaleX, scaleY, glyph, &w, &h, &x, &y); + + offsetX = x; + offsetY = y; + + return new Bitmap((IntPtr)ptr, w, h, FreeBitmap); + } + + public Bitmap GetGlyphBitmapSubpixel(float scaleX, float scaleY, float shiftX, float shiftY, int glyph, out int offsetX, out int offsetY) + { + int w, h, x, y; + void* ptr = Stbtt.GetGlyphBitmapSubpixel(_info, scaleX, scaleY, shiftX, shiftY, glyph, &w, &h, &x, &y); + + offsetX = x; + offsetY = y; + + return new Bitmap((IntPtr)ptr, w, h, FreeBitmap); + } + + public Bitmap GetGlyphSdf(float scale, int glyph, int padding, byte edgeValue, float pixelDistScale, out int offsetX, out int offsetY) + { + int w, h, x, y; + void *ptr = Stbtt.GetGlyphSDF(_info, scale, glyph, padding, edgeValue, pixelDistScale, &w, &h, &x, &y); + + offsetX = x; + offsetY = y; + + return new Bitmap((IntPtr)ptr, w, h, FreeSdf); + } + + public Bitmap GetCodepointSdf(float scale, int codepoint, int padding, byte edgeValue, float pixelDistScale, out int offsetX, out int offsetY) + { + int w, h, x, y; + void *ptr = Stbtt.GetCodepointSDF(_info, scale, codepoint, padding, edgeValue, pixelDistScale, &w, &h, &x, &y); + + offsetX = x; + offsetY = y; + + return new Bitmap((IntPtr)ptr, w, h, FreeSdf); + } + + public Bitmap GetCodepointSdf(float scale, Rune codepoint, int padding, byte edgeValue, float pixelDistScale, out int offsetX, out int offsetY) + => GetCodepointSdf(scale, codepoint.Value, padding, edgeValue, pixelDistScale, out offsetX, out offsetY); + + public void Dispose() + { + Dispose(true); + } + + bool isDisposed = false; + private void Dispose(bool disposing) + { + if (isDisposed) return; + + if (disposing) + { + GC.SuppressFinalize(this); + } + + + Marshal.FreeHGlobal(_buffer); + Marshal.FreeHGlobal((IntPtr)_info); + isDisposed = true; + } + + public static bool TryLoad(Stream stream, out StbFont font) + { + byte* buffer = (byte*)Marshal.AllocHGlobal((int)stream.Length); + stbtt_fontinfo* fontInfo = (stbtt_fontinfo*)Marshal.AllocHGlobal(sizeof(stbtt_fontinfo)); + + stream.Read(new Span(buffer, (int)stream.Length)); + + int nfont = Stbtt.GetNumberOfFonts(buffer); + + if (nfont == 0) + { + font = null; + return false; + } + + int offset = Stbtt.GetFontOffsetForIndex(buffer, 0); + + if (Stbtt.InitFont(fontInfo, (byte*)buffer, offset) == 0) + { + Marshal.FreeHGlobal((IntPtr)buffer); + Marshal.FreeHGlobal((IntPtr)fontInfo); + + font = null; + return false; + } + + font = new StbFont((IntPtr)buffer, fontInfo); + return true; + } + + public static StbFont Load(Stream stream) + { + if (TryLoad(stream, out StbFont font)) + { + return font; + } + + throw new Exception("Could not load the font."); + } + + private static void FreeBitmap(IntPtr buffer) + { + Stbtt.FreeBitmap((byte*)buffer, null); + } + + private static void FreeSdf(IntPtr buffer) + { + Stbtt.FreeSDF((byte*)buffer, null); + } + + public struct Box + { + public int X0; + public int Y0; + public int X1; + public int Y1; + + public Box(int x0, int y0, int x1, int y1) + { + X0 = x0; Y0 = y0; + X1 = x1; Y1 = y1; + } + } + + public class Bitmap : IDisposable + { + public IntPtr Buffer { get; } + public int Width { get; } + public int Height { get; } + + private readonly Action Destroy; + + public Bitmap(IntPtr buffer, int width, int height, Action destroy) + { + Buffer = buffer; + Width = width; + Height = height; + Destroy = destroy; + } + ~Bitmap() + { + Dispose(false); + } + + public void Dispose() => Dispose(true); + + private bool isDiposed = false; + public void Dispose(bool disposing) + { + if (isDiposed) return; + + if (disposing) + { + GC.SuppressFinalize(this); + } + + Destroy(Buffer); + isDiposed = true; + } + } + } +} \ No newline at end of file diff --git a/Quik.StbTrueType/Stbtt.Manual.cs b/Quik.StbTrueType/Stbtt.Manual.cs new file mode 100644 index 0000000..a0862e8 --- /dev/null +++ b/Quik.StbTrueType/Stbtt.Manual.cs @@ -0,0 +1,63 @@ +using System; +using System.Runtime.InteropServices; +using System.Reflection; + +namespace Quik.Stb +{ + public unsafe static partial class Stbtt + { + private delegate void FailedAssertProc(byte *expression, byte *file, int line, byte *function); + + private static readonly string[] LibraryNames = new string[] + { + //FIXME: This is wrong on so many levels, but, i need to do this + // in order to get a change of this running. + "runtimes/win-x64/native/libstbtt.dll", + "runtimes/win-x86/native/libstbtt.dll", + "runtimes/linux-arm/native/libstbtt.so", + "runtimes/linux-arm64/native/libstbtt.so", + "runtimes/linux-x64/native/libstbtt.so", + "runtimes/native/libstbtt.dylib", + "libstbtt.dll", + "libstbtt.so", + "libstbtt.dylib", + }; + + static Stbtt() + { + NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), Resolver); + + quik_stbtt_failed_assert_store(Marshal.GetFunctionPointerForDelegate(FailedAssert)); + } + + private static IntPtr Resolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) + { + if (libraryName != "stbtt") + return IntPtr.Zero; + + foreach (string name in LibraryNames) + { + if (NativeLibrary.TryLoad(name, assembly, searchPath, out IntPtr handle)) + { + return handle; + } + } + + return NativeLibrary.Load(libraryName); + } + + private static void FailedAssert(byte *expression, byte *file, int line, byte *function) + { + string expr = expression == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)expression); + string f = file == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)file); + string func = function == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)function); + + Exception ex = new Exception("Assert failed in native stbtt code."); + ex.Data.Add("Expression", expr); + ex.Data.Add("File", f); + ex.Data.Add("Line", line); + ex.Data.Add("Function", func); + throw ex; + } + } +} \ No newline at end of file diff --git a/Quik.sln b/Quik.sln index c0bdfd7..604b4c1 100644 --- a/Quik.sln +++ b/Quik.sln @@ -9,12 +9,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quik.StbImage", "Quik.StbIm EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quik.StbTrueType", "Quik.StbTrueType\Quik.StbTrueType.csproj", "{AD5B02B0-F957-4816-8CE9-5F278B856C70}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{5E87AF9C-AC12-4E48-99B1-CBEC0C97B624}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quik.StbImage.Tests", "tests\Quik.StbImage.Tests\Quik.StbImage.Tests.csproj", "{AFF181CF-D51E-4E16-B3C6-38ED1E1FF615}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quik.Media.Stb", "Quik.Media.Stb\Quik.Media.Stb.csproj", "{3D354BE0-42A7-45C4-AAEA-B0F8963A5745}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{AE05ADE5-A809-479F-97D5-BEAFE7604285}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quik.Stb.Tests", "tests\Quik.Stb.Tests\Quik.Stb.Tests.csproj", "{BC7D3002-B79B-4141-B6CC-74FB2175B474}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -73,18 +73,6 @@ Global {AD5B02B0-F957-4816-8CE9-5F278B856C70}.Release|x64.Build.0 = Release|Any CPU {AD5B02B0-F957-4816-8CE9-5F278B856C70}.Release|x86.ActiveCfg = Release|Any CPU {AD5B02B0-F957-4816-8CE9-5F278B856C70}.Release|x86.Build.0 = Release|Any CPU - {AFF181CF-D51E-4E16-B3C6-38ED1E1FF615}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AFF181CF-D51E-4E16-B3C6-38ED1E1FF615}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AFF181CF-D51E-4E16-B3C6-38ED1E1FF615}.Debug|x64.ActiveCfg = Debug|Any CPU - {AFF181CF-D51E-4E16-B3C6-38ED1E1FF615}.Debug|x64.Build.0 = Debug|Any CPU - {AFF181CF-D51E-4E16-B3C6-38ED1E1FF615}.Debug|x86.ActiveCfg = Debug|Any CPU - {AFF181CF-D51E-4E16-B3C6-38ED1E1FF615}.Debug|x86.Build.0 = Debug|Any CPU - {AFF181CF-D51E-4E16-B3C6-38ED1E1FF615}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AFF181CF-D51E-4E16-B3C6-38ED1E1FF615}.Release|Any CPU.Build.0 = Release|Any CPU - {AFF181CF-D51E-4E16-B3C6-38ED1E1FF615}.Release|x64.ActiveCfg = Release|Any CPU - {AFF181CF-D51E-4E16-B3C6-38ED1E1FF615}.Release|x64.Build.0 = Release|Any CPU - {AFF181CF-D51E-4E16-B3C6-38ED1E1FF615}.Release|x86.ActiveCfg = Release|Any CPU - {AFF181CF-D51E-4E16-B3C6-38ED1E1FF615}.Release|x86.Build.0 = Release|Any CPU {3D354BE0-42A7-45C4-AAEA-B0F8963A5745}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3D354BE0-42A7-45C4-AAEA-B0F8963A5745}.Debug|Any CPU.Build.0 = Debug|Any CPU {3D354BE0-42A7-45C4-AAEA-B0F8963A5745}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -97,8 +85,20 @@ Global {3D354BE0-42A7-45C4-AAEA-B0F8963A5745}.Release|x64.Build.0 = Release|Any CPU {3D354BE0-42A7-45C4-AAEA-B0F8963A5745}.Release|x86.ActiveCfg = Release|Any CPU {3D354BE0-42A7-45C4-AAEA-B0F8963A5745}.Release|x86.Build.0 = Release|Any CPU + {BC7D3002-B79B-4141-B6CC-74FB2175B474}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BC7D3002-B79B-4141-B6CC-74FB2175B474}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BC7D3002-B79B-4141-B6CC-74FB2175B474}.Debug|x64.ActiveCfg = Debug|Any CPU + {BC7D3002-B79B-4141-B6CC-74FB2175B474}.Debug|x64.Build.0 = Debug|Any CPU + {BC7D3002-B79B-4141-B6CC-74FB2175B474}.Debug|x86.ActiveCfg = Debug|Any CPU + {BC7D3002-B79B-4141-B6CC-74FB2175B474}.Debug|x86.Build.0 = Debug|Any CPU + {BC7D3002-B79B-4141-B6CC-74FB2175B474}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BC7D3002-B79B-4141-B6CC-74FB2175B474}.Release|Any CPU.Build.0 = Release|Any CPU + {BC7D3002-B79B-4141-B6CC-74FB2175B474}.Release|x64.ActiveCfg = Release|Any CPU + {BC7D3002-B79B-4141-B6CC-74FB2175B474}.Release|x64.Build.0 = Release|Any CPU + {BC7D3002-B79B-4141-B6CC-74FB2175B474}.Release|x86.ActiveCfg = Release|Any CPU + {BC7D3002-B79B-4141-B6CC-74FB2175B474}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution - {AFF181CF-D51E-4E16-B3C6-38ED1E1FF615} = {5E87AF9C-AC12-4E48-99B1-CBEC0C97B624} + {BC7D3002-B79B-4141-B6CC-74FB2175B474} = {AE05ADE5-A809-479F-97D5-BEAFE7604285} EndGlobalSection EndGlobal diff --git a/tests/Quik.Stb.Tests/LoadFont.cs b/tests/Quik.Stb.Tests/LoadFont.cs new file mode 100644 index 0000000..c676746 --- /dev/null +++ b/tests/Quik.Stb.Tests/LoadFont.cs @@ -0,0 +1,72 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.IO; +using System.Runtime.InteropServices; +using Quik.Stb; + +namespace Quik.Stb +{ + [TestClass] + [TestCategory("Load Font")] + public class LoadFont + { + StbFont? font; + + [TestInitialize] + public void Initialize() + { + using (Stream? str = GetType().Assembly.GetManifestResourceStream("Quik.Stb.Tests.res.Varicka.ttf")) + { + Assert.IsNotNull(str, "Test font file not packed."); + font = StbFont.Load(str); + } + } + + [TestCleanup] + public void Deinitialize() + { + font?.Dispose(); + } + + [TestMethod] + public void AscendIsValid() + { + Assert.AreNotEqual(-1, font!.Ascend); + } + + [TestMethod] + public void DescendIsValid() + { + Assert.AreNotEqual(-1, font!.Descend); + } + + [TestMethod] + public void VLineGapIsValid() + { + Assert.AreNotEqual(-1, font!.VerticalLineGap); + } + + [TestMethod] + public void BBoxIsValid() + { + Assert.AreNotEqual(default, font!.BoundingBox); + } + + [TestMethod] + public void KerningTableIsValid() + { + Assert.IsNotNull(font!.KerningTable); + } + + [TestMethod] + public void GetGlyphsForAscii() + { + for (int i = 0; i < 128; i++) + { + int glyph = font!.FindGlyphIndex(i); + + Assert.AreNotEqual(-1, glyph); + } + } + } +} \ No newline at end of file diff --git a/tests/Quik.StbImage.Tests/LoadImage.cs b/tests/Quik.Stb.Tests/LoadImage.cs similarity index 70% rename from tests/Quik.StbImage.Tests/LoadImage.cs rename to tests/Quik.Stb.Tests/LoadImage.cs index 0d0628c..9a7157e 100644 --- a/tests/Quik.StbImage.Tests/LoadImage.cs +++ b/tests/Quik.Stb.Tests/LoadImage.cs @@ -1,11 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; using System.IO; -using System.Runtime.InteropServices; -using Quik.Stb; -using Image = Quik.Stb.StbImage; -namespace Quik.StbImage.Tests +namespace Quik.Stb.Tests { [TestClass] [TestCategory("Load Image")] @@ -27,7 +23,7 @@ namespace Quik.StbImage.Tests private unsafe void TestImage(string path, int width, int height) { - Image image = Image.Load(GetImage(path)); + StbImage image = StbImage.Load(GetImage(path)); Assert.IsNotNull(image); @@ -41,9 +37,9 @@ namespace Quik.StbImage.Tests const int HEIGHT = 512; [TestMethod("Load a single frame GIF")] - public unsafe void LoadGif() => TestImage("Quik.StbImage.Tests.res.kodim.kodim23.gif", WIDTH, HEIGHT); + public unsafe void LoadGif() => TestImage("Quik.Stb.Tests.res.kodim.kodim23.gif", WIDTH, HEIGHT); [TestMethod("Load a JPEG")] - public unsafe void LoadJpg() => TestImage("Quik.StbImage.Tests.res.kodim.kodim23.jpg", WIDTH, HEIGHT); - [TestMethod("Load a PNG")] public unsafe void LoadPng() => TestImage("Quik.StbImage.Tests.res.kodim.kodim23.png", WIDTH, HEIGHT); + public unsafe void LoadJpg() => TestImage("Quik.Stb.Tests.res.kodim.kodim23.jpg", WIDTH, HEIGHT); + [TestMethod("Load a PNG")] public unsafe void LoadPng() => TestImage("Quik.Stb.Tests.res.kodim.kodim23.png", WIDTH, HEIGHT); } } \ No newline at end of file diff --git a/tests/Quik.StbImage.Tests/Quik.StbImage.Tests.csproj b/tests/Quik.Stb.Tests/Quik.Stb.Tests.csproj similarity index 90% rename from tests/Quik.StbImage.Tests/Quik.StbImage.Tests.csproj rename to tests/Quik.Stb.Tests/Quik.Stb.Tests.csproj index 899d952..2c11c33 100644 --- a/tests/Quik.StbImage.Tests/Quik.StbImage.Tests.csproj +++ b/tests/Quik.Stb.Tests/Quik.Stb.Tests.csproj @@ -23,6 +23,7 @@ + diff --git a/tests/Quik.Stb.Tests/res/Varicka.ttf b/tests/Quik.Stb.Tests/res/Varicka.ttf new file mode 100644 index 0000000000000000000000000000000000000000..4c16d09d709c57e89cfc6b0bfce5685e1bed4242 GIT binary patch literal 40624 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 literal 0 HcmV?d00001 diff --git a/tests/Quik.Stb.Tests/res/Varicka.txt b/tests/Quik.Stb.Tests/res/Varicka.txt new file mode 100644 index 0000000..12337b2 --- /dev/null +++ b/tests/Quik.Stb.Tests/res/Varicka.txt @@ -0,0 +1,12 @@ +Varicka (Truetype and Opentype with no OT features) + +Based on 'Varick', from "Decorative Condensed Alphabets", by Dan Solo, Page 94. + +Letter spacing is set to balance the letterforms themselves; the space between most adjacent letters is identical to the horizontal space inside such letters as 'O' and 'H'. Kerning is supplied as needed for certain letter combinations, particularly those that have a letter with a projection on the left, such as A, E, F, H, K, P, and R. + +Varicka is superficially similar to Red Rooster's Triple Gothic Condensed, but the Solo book's font has different features and some very different letterforms. + +This font is free and available for all use, personal and commercial, with no restrictions. + + Character + February 13, 2010 \ No newline at end of file diff --git a/tests/Quik.StbImage.Tests/res/kodim/LICENSE.md b/tests/Quik.Stb.Tests/res/kodim/LICENSE.md similarity index 100% rename from tests/Quik.StbImage.Tests/res/kodim/LICENSE.md rename to tests/Quik.Stb.Tests/res/kodim/LICENSE.md diff --git a/tests/Quik.StbImage.Tests/res/kodim/kodim23.gif b/tests/Quik.Stb.Tests/res/kodim/kodim23.gif similarity index 100% rename from tests/Quik.StbImage.Tests/res/kodim/kodim23.gif rename to tests/Quik.Stb.Tests/res/kodim/kodim23.gif diff --git a/tests/Quik.StbImage.Tests/res/kodim/kodim23.jpg b/tests/Quik.Stb.Tests/res/kodim/kodim23.jpg similarity index 100% rename from tests/Quik.StbImage.Tests/res/kodim/kodim23.jpg rename to tests/Quik.Stb.Tests/res/kodim/kodim23.jpg diff --git a/tests/Quik.StbImage.Tests/res/kodim/kodim23.png b/tests/Quik.Stb.Tests/res/kodim/kodim23.png similarity index 100% rename from tests/Quik.StbImage.Tests/res/kodim/kodim23.png rename to tests/Quik.Stb.Tests/res/kodim/kodim23.png diff --git a/tests/Quik.StbImage.Tests/res/kodim/kodim23.tiff b/tests/Quik.Stb.Tests/res/kodim/kodim23.tiff similarity index 100% rename from tests/Quik.StbImage.Tests/res/kodim/kodim23.tiff rename to tests/Quik.Stb.Tests/res/kodim/kodim23.tiff diff --git a/tests/Quik.StbImage.Tests/res/kodim/kodim23.webp b/tests/Quik.Stb.Tests/res/kodim/kodim23.webp similarity index 100% rename from tests/Quik.StbImage.Tests/res/kodim/kodim23.webp rename to tests/Quik.Stb.Tests/res/kodim/kodim23.webp