108 lines
3.6 KiB
C#
108 lines
3.6 KiB
C#
|
using System;
|
||
|
using System.IO;
|
||
|
using Quik.FreeType;
|
||
|
using Quik.Media;
|
||
|
using Quik.Media.Color;
|
||
|
|
||
|
namespace Quik.Media.Defaults
|
||
|
{
|
||
|
public class QFontFreeType : QFont
|
||
|
{
|
||
|
private MemoryStream ms;
|
||
|
private FTFace face;
|
||
|
|
||
|
public override FontInfo Info => throw new NotImplementedException();
|
||
|
|
||
|
public QFontFreeType(Stream stream)
|
||
|
{
|
||
|
ms = new MemoryStream();
|
||
|
stream.CopyTo(ms);
|
||
|
|
||
|
FTError e = FT.NewMemoryFace(Ft, ms.GetBuffer(), ms.Length, 0, out face);
|
||
|
if (e != FTError.None)
|
||
|
{
|
||
|
throw new Exception("Could not load font face from stream.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override bool HasRune(int rune)
|
||
|
{
|
||
|
return FT.GetCharIndex(face, (ulong)rune) != 0;
|
||
|
}
|
||
|
|
||
|
public override QFontPage RasterizePage(int codepage, float size, in FontRasterizerOptions options)
|
||
|
{
|
||
|
FT.SetCharSize(face, 0, (long)Math.Round(64*size), 0, (uint)Math.Round(options.Resolution));
|
||
|
QGlyphMetrics[] allMetrics = new QGlyphMetrics[256];
|
||
|
|
||
|
// Figure out the map size needed.
|
||
|
int pixels = 0;
|
||
|
for (int i = 0; i < 256; i++)
|
||
|
{
|
||
|
uint index = FT.GetCharIndex(face, (ulong)(codepage + i));
|
||
|
FT.LoadGlyph(face, index, FTLoadFlags.Default);
|
||
|
|
||
|
ref readonly FTGlyphMetrics metrics = ref face.Glyph.Metrics;
|
||
|
allMetrics[i] = new QGlyphMetrics(
|
||
|
codepage + i,
|
||
|
new QVec2(metrics.Width, metrics.Height),
|
||
|
new QVec2(metrics.HorizontalBearingX/64f, metrics.HorizontalBearingY/64f),
|
||
|
new QVec2(metrics.VerticalBearingX/64f, metrics.VerticalBearingY/64f),
|
||
|
new QVec2(metrics.HorizontalAdvance/64f, metrics.VerticalAdvance/64f)
|
||
|
);
|
||
|
pixels = (int)Math.Max(pixels, Math.Max(face.Glyph.Metrics.Width/64f, face.Glyph.Metrics.Height/64f));
|
||
|
}
|
||
|
|
||
|
int bits = Math.ILogB(pixels);
|
||
|
if (1 << bits != pixels)
|
||
|
{
|
||
|
pixels = 1 << bits + 1;
|
||
|
}
|
||
|
|
||
|
// Now we can create a bitmap and render our glyphs.
|
||
|
|
||
|
QImageBuffer buffer = new QImageBuffer(QImageFormat.RedU8, (int)pixels, (int)pixels, 256);
|
||
|
|
||
|
for (int i = 0; i < 256; i++)
|
||
|
{
|
||
|
uint index = FT.GetCharIndex(face, (ulong)(codepage + i));
|
||
|
FT.LoadGlyph(face, index, FTLoadFlags.Default);
|
||
|
FT.RenderGlyph(face.Glyph, options.Sdf ? FTRenderMode.Sdf : FTRenderMode.Mono);
|
||
|
|
||
|
ref readonly FTBitmap bitmap = ref face.Glyph.Bitmap;
|
||
|
|
||
|
buffer.LockBits3d(out QImageLock dst, QImageLockOptions.Default, i);
|
||
|
|
||
|
if (bitmap.Buffer != IntPtr.Zero) unsafe
|
||
|
{
|
||
|
for (int j = 0; j < bitmap.Rows; j++)
|
||
|
{
|
||
|
Buffer.MemoryCopy(
|
||
|
(byte*)bitmap.Buffer + (j * bitmap.Pitch),
|
||
|
(byte*)dst.ImagePtr + (j * dst.Width),
|
||
|
dst.Width,
|
||
|
bitmap.Width);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
buffer.UnlockBits();
|
||
|
}
|
||
|
|
||
|
return new QFontPage(this, codepage, size, options, buffer, allMetrics);
|
||
|
}
|
||
|
|
||
|
protected override void Dispose(bool disposing)
|
||
|
{
|
||
|
base.Dispose(disposing);
|
||
|
|
||
|
if (disposing)
|
||
|
{
|
||
|
ms.Dispose();
|
||
|
}
|
||
|
|
||
|
FT.DoneFace(face);
|
||
|
}
|
||
|
|
||
|
private static FTLibrary Ft => FTProvider.Ft;
|
||
|
}
|
||
|
}
|