Dashboard/Quik.FreeType/FreeTypeFont.cs
H. Utku Maden 9339295378 Push all uncommitted changes.
I have had a long break from this project due to other higher priority
things going on in my life. Big changes inbound.
2023-05-13 16:17:57 +03:00

181 lines
5.6 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using Quik.Typography;
namespace Quik.FreeType
{
public class FreeTypeFont : QuikFont, IDisposable
{
private FreeTypeFontManager _owner;
private FTLibrary _library;
private FTFace _face;
private List<Atlas> _textures = new List<Atlas>();
private Dictionary<int, GlyphEntry> _entries = new Dictionary<int, GlyphEntry>();
public override float Ascender => _face.ScaledSize.Ascender / 64f;
public override float Descender => _face.ScaledSize.Descender / 64f;
internal FreeTypeFont(FreeTypeFontManager owner, FileInfo file, QuikFontStyle style)
{
Style = style;
_owner = owner;
_library = owner._library;
if (FT.NewFace(_library, file.FullName, 0, out _face) != FTError.None)
{
throw new Exception("Could not load font file.");
}
FT.SetCharSize(_face, 0, (long)(style.Size * 64), 96, 96);
_textures.Add(new Atlas(owner.Context.TextureManager, _library));
}
public override QuikFontStyle Style { get; }
public override bool HasCharacter(int character)
{
return FT.GetCharIndex(_face, (ulong)character) != 0;
}
public override void GetCharacter(int character, out IQuikTexture texture, out QuikGlyph glyph)
{
GlyphEntry entry;
if (_entries.TryGetValue(character, out entry))
{
texture = entry.Atlas.Texture;
glyph = entry.Metrics;
return;
}
entry = new GlyphEntry();
entry.Character = character;
uint index = FT.GetCharIndex(_face, (ulong) character);
FT.LoadGlyph(_face, index, FTLoadFlags.Default);
FT.RenderGlyph(_face.Glyph, FTRenderMode.Normal);
entry.Atlas = _textures[_textures.Count - 1];
if (!entry.Atlas.CanFit(_face.Glyph))
{
entry.Atlas = new Atlas(_owner.Context.TextureManager, _library);
_textures.Add(entry.Atlas);
}
entry.Atlas.AttachGlyph(_face.Glyph, out QuikRectangle uvs);
entry.Metrics = new QuikGlyph(
character,
uvs,
new QuikVec2(_face.Glyph.Metrics.Width / 64f, _face.Glyph.Metrics.Height / 64f),
new QuikVec2(_face.Glyph.Metrics.HorizontalBearingX / 64f, _face.Glyph.Metrics.HorizontalBearingY / 64f),
new QuikVec2(_face.Glyph.Metrics.VerticalBearingX / 64f , _face.Glyph.Metrics.VerticalBearingY / 64f),
new QuikVec2(_face.Glyph.Metrics.HorizontalAdvance / 64f, _face.Glyph.Metrics.VerticalAdvance / 64f));
_entries[character] = entry;
texture = entry.Atlas.Texture;
glyph = entry.Metrics;
}
public bool IsDisposed { get; private set; } = false;
private void Dispose(bool disposing)
{
if (IsDisposed) return;
if (disposing)
{
foreach (Atlas atlas in _textures)
{
atlas.Dispose();
}
}
FT.DoneFace(_face);
IsDisposed = true;
}
public void Dispose() => Dispose(true);
private class Atlas : IDisposable
{
public IQuikTexture Texture;
private QuikVec2 _pointer = new QuikVec2();
private float _verticalAdvance = 0;
private FTLibrary _ft;
private FTBitmap _bitmap;
public Atlas(IQuikTextureManager textureManager, FTLibrary ft)
{
Texture = textureManager.CreateTexture(
new QuikVec2(4096, 4096),
false,
QuikImageFormat.RgbaU8);
FT.BitmapInit(ref _bitmap);
_ft = ft;
}
public bool CanFit(in FTGlyphSlot slot)
{
// FIXME: the atlas will overflow.
return true;
}
public void AttachGlyph(in FTGlyphSlot slot, out QuikRectangle UVs)
{
FT.BitmapConvert(_ft, slot.Bitmap, ref _bitmap, 1);
QuikRectangle position =
new QuikRectangle(
_pointer + new QuikVec2(_bitmap.Width + 1, _bitmap.Rows + 1),
_pointer + new QuikVec2(1, 1));
Texture.SubImage(
_bitmap.Buffer,
QuikImageFormat.AlphaU8,
position,
0,
1);
_pointer.X += _bitmap.Width + 2;
_verticalAdvance = Math.Max(_verticalAdvance, slot.Bitmap.Rows + 2);
UVs = new QuikRectangle(
position.Right / 4096,
position.Bottom / 4096,
position.Left / 4096,
position.Top / 4096
);
}
private bool _isDisposed = false;
public void Dispose(bool disposing)
{
if (_isDisposed) return;
if (disposing)
{
Texture.Dispose();
}
FT.BitmapDone(_ft, ref _bitmap);
_isDisposed = true;
}
public void Dispose()
{
}
}
private class GlyphEntry
{
public int Character;
public Atlas Atlas;
public QuikGlyph Metrics;
}
}
}