2023-06-29 13:17:32 +02:00
|
|
|
using System;
|
2024-04-28 12:45:46 +02:00
|
|
|
using System.Collections.Generic;
|
2024-05-15 22:17:01 +02:00
|
|
|
using System.Runtime.InteropServices;
|
2024-04-28 12:45:46 +02:00
|
|
|
using Quik.Media;
|
|
|
|
using Quik.Media.Font;
|
2023-06-29 13:17:32 +02:00
|
|
|
|
|
|
|
namespace Quik.Media
|
|
|
|
{
|
|
|
|
/// <summary>
|
|
|
|
/// Abstract class that represents a font.
|
|
|
|
/// </summary>
|
|
|
|
public abstract class QFont : IDisposable
|
|
|
|
{
|
2024-04-28 12:45:46 +02:00
|
|
|
public abstract FontFace Face { get; }
|
|
|
|
public string Family => Face.Family;
|
|
|
|
public FontSlant Slant => Face.Slant;
|
|
|
|
public FontWeight Weight => Face.Weight;
|
|
|
|
public FontStretch Stretch => Face.Stretch;
|
2023-06-29 13:17:32 +02:00
|
|
|
|
|
|
|
public abstract bool HasRune(int rune);
|
2024-05-15 22:17:01 +02:00
|
|
|
protected abstract QImage Render(out QGlyphMetrics metrics, int codepoint, float size, in FontRasterizerOptions options);
|
|
|
|
|
|
|
|
|
|
|
|
private readonly Dictionary<float, SizedFontCollection> _atlasses = new Dictionary<float, SizedFontCollection>();
|
|
|
|
|
|
|
|
public void Get(int codepoint, float size, out FontGlyph glyph)
|
|
|
|
{
|
|
|
|
SizedFontCollection collection;
|
|
|
|
|
|
|
|
if (!_atlasses.TryGetValue(size, out collection))
|
|
|
|
{
|
|
|
|
collection = new SizedFontCollection(size);
|
|
|
|
_atlasses.Add(size, collection);
|
|
|
|
}
|
|
|
|
|
|
|
|
collection.Get(codepoint, out glyph, this);
|
|
|
|
}
|
2023-06-29 13:17:32 +02:00
|
|
|
|
|
|
|
// IDisposable
|
|
|
|
private bool isDisposed = false;
|
|
|
|
private void DisposePrivate(bool disposing)
|
|
|
|
{
|
|
|
|
if (isDisposed) return;
|
|
|
|
|
|
|
|
Dispose(disposing);
|
|
|
|
|
|
|
|
isDisposed = true;
|
|
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing) { }
|
|
|
|
public void Dispose() => DisposePrivate(true);
|
2023-09-22 18:30:17 +02:00
|
|
|
|
2024-05-15 22:17:01 +02:00
|
|
|
private class SizedFontCollection
|
2023-09-22 18:30:17 +02:00
|
|
|
{
|
2024-05-15 22:17:01 +02:00
|
|
|
public float Size { get; }
|
|
|
|
private readonly Dictionary<int, FontGlyph> glyphs = new Dictionary<int, FontGlyph>();
|
|
|
|
private readonly FontAtlas atlas;
|
2023-09-22 18:30:17 +02:00
|
|
|
|
2024-05-15 22:17:01 +02:00
|
|
|
public SizedFontCollection(float size)
|
|
|
|
{
|
|
|
|
Size = size;
|
2023-09-22 18:30:17 +02:00
|
|
|
|
2024-05-15 22:17:01 +02:00
|
|
|
QuikApplication.Current.Platform.GetMaximumImage(out int height, out int width);
|
2023-09-22 18:30:17 +02:00
|
|
|
|
2024-05-15 22:17:01 +02:00
|
|
|
// Do no allow to create a texture that is greater than 16 square characters at 200 DPI.
|
|
|
|
width = Math.Min(width, (int)(size * 200 * 16));
|
|
|
|
height = Math.Min(height, (int)(size * 200 * 16));
|
|
|
|
// width = height = 256;
|
2023-09-22 18:30:17 +02:00
|
|
|
|
2024-05-15 22:17:01 +02:00
|
|
|
atlas = new FontAtlas(width, height, QuikApplication.Current.FontProvider.RasterizerOptions.Sdf);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Get(int codepoint, out FontGlyph glyph, QFont font)
|
|
|
|
{
|
|
|
|
if (glyphs.TryGetValue(codepoint, out glyph))
|
|
|
|
return;
|
|
|
|
|
|
|
|
QImage image = font.Render(
|
|
|
|
out QGlyphMetrics metrics,
|
|
|
|
codepoint,
|
|
|
|
Size,
|
|
|
|
QuikApplication.Current.FontProvider.RasterizerOptions);
|
|
|
|
|
|
|
|
if (image != null)
|
|
|
|
{
|
|
|
|
image.LockBits2d(out QImageLock l, QImageLockOptions.Default);
|
|
|
|
atlas.PutGlyph(codepoint, l, out FontAtlasGlyphInfo glyphInfo);
|
|
|
|
image.UnlockBits();
|
|
|
|
image.Dispose();
|
|
|
|
|
|
|
|
glyph = new FontGlyph(codepoint, glyphInfo.Image, metrics, glyphInfo.UVs);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glyph = new FontGlyph(codepoint, null, metrics, default);
|
|
|
|
}
|
|
|
|
|
|
|
|
glyphs[codepoint] = glyph;
|
|
|
|
}
|
2023-09-22 18:30:17 +02:00
|
|
|
}
|
2024-05-15 22:17:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public readonly struct FontGlyph
|
|
|
|
{
|
|
|
|
public readonly int CodePoint;
|
|
|
|
public readonly QImage Image;
|
|
|
|
public readonly QGlyphMetrics Metrics;
|
|
|
|
public readonly QRectangle UVs;
|
|
|
|
|
|
|
|
public FontGlyph(int codepoint, QImage image, in QGlyphMetrics metrics, in QRectangle uvs)
|
2023-09-22 18:30:17 +02:00
|
|
|
{
|
2024-05-15 22:17:01 +02:00
|
|
|
CodePoint = codepoint;
|
2023-09-22 18:30:17 +02:00
|
|
|
Image = image;
|
|
|
|
Metrics = metrics;
|
2024-05-15 22:17:01 +02:00
|
|
|
UVs = uvs;
|
2023-09-22 18:30:17 +02:00
|
|
|
}
|
2024-05-15 22:17:01 +02:00
|
|
|
}
|
2023-09-22 18:30:17 +02:00
|
|
|
|
2024-05-15 22:17:01 +02:00
|
|
|
public struct FontRasterizerOptions
|
|
|
|
{
|
|
|
|
public float Resolution { get; set; }
|
|
|
|
public bool Sdf { get; set; }
|
2023-09-22 18:30:17 +02:00
|
|
|
|
2024-05-15 22:17:01 +02:00
|
|
|
public static readonly FontRasterizerOptions Default = new FontRasterizerOptions()
|
2023-09-22 18:30:17 +02:00
|
|
|
{
|
2024-05-15 22:17:01 +02:00
|
|
|
Resolution = 96.0f,
|
|
|
|
Sdf = false
|
|
|
|
};
|
2023-09-22 18:30:17 +02:00
|
|
|
}
|
2023-06-29 13:17:32 +02:00
|
|
|
}
|