Compare commits
No commits in common. "bd69c0d93f75ea6fbcdf48bbb80810daee1377fa" and "7cb47c721b1aa474763fcb2a953a63f9d9fd4678" have entirely different histories.
bd69c0d93f
...
7cb47c721b
@ -1,9 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Quik.FreeType;
|
using Quik.FreeType;
|
||||||
|
using Quik.Media;
|
||||||
using Quik.Media.Color;
|
using Quik.Media.Color;
|
||||||
using Quik.Media.Font;
|
|
||||||
|
|
||||||
namespace Quik.Media.Defaults
|
namespace Quik.Media.Defaults
|
||||||
{
|
{
|
||||||
@ -12,7 +11,7 @@ namespace Quik.Media.Defaults
|
|||||||
private MemoryStream ms;
|
private MemoryStream ms;
|
||||||
private FTFace face;
|
private FTFace face;
|
||||||
|
|
||||||
public override FontFace Face => throw new NotImplementedException();
|
public override FontInfo Info => throw new NotImplementedException();
|
||||||
|
|
||||||
public QFontFreeType(Stream stream)
|
public QFontFreeType(Stream stream)
|
||||||
{
|
{
|
||||||
@ -31,39 +30,65 @@ namespace Quik.Media.Defaults
|
|||||||
return FT.GetCharIndex(face, (ulong)rune) != 0;
|
return FT.GetCharIndex(face, (ulong)rune) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override QImage Render(out QGlyphMetrics metrics, int codepoint, float size, in FontRasterizerOptions options)
|
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));
|
FT.SetCharSize(face, 0, (long)Math.Round(64*size), 0, (uint)Math.Round(options.Resolution));
|
||||||
|
QGlyphMetrics[] allMetrics = new QGlyphMetrics[256];
|
||||||
|
|
||||||
uint index = FT.GetCharIndex(face, (ulong)codepoint);
|
// 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);
|
FT.LoadGlyph(face, index, FTLoadFlags.Default);
|
||||||
|
|
||||||
ref readonly FTGlyphMetrics ftmetrics = ref face.Glyph.Metrics;
|
ref readonly FTGlyphMetrics metrics = ref face.Glyph.Metrics;
|
||||||
metrics = new QGlyphMetrics(codepoint,
|
allMetrics[i] = new QGlyphMetrics(
|
||||||
new QVec2(ftmetrics.Width/64f, ftmetrics.Height/64f),
|
codepage + i,
|
||||||
new QVec2(ftmetrics.HorizontalBearingX/64f, ftmetrics.HorizontalBearingY/64f),
|
new QVec2(metrics.Width, metrics.Height),
|
||||||
new QVec2(ftmetrics.VerticalBearingX/64f, ftmetrics.VerticalBearingY/64f),
|
new QVec2(metrics.HorizontalBearingX/64f, metrics.HorizontalBearingY/64f),
|
||||||
new QVec2(ftmetrics.HorizontalAdvance/64f, ftmetrics.VerticalAdvance/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);
|
||||||
|
|
||||||
FT.RenderGlyph(face.Glyph, options.Sdf ? FTRenderMode.Sdf : FTRenderMode.Normal);
|
|
||||||
ref readonly FTBitmap bitmap = ref face.Glyph.Bitmap;
|
ref readonly FTBitmap bitmap = ref face.Glyph.Bitmap;
|
||||||
|
|
||||||
if (bitmap.Width == 0 || bitmap.Pitch == 0 || bitmap.Buffer == IntPtr.Zero)
|
buffer.LockBits3d(out QImageLock dst, QImageLockOptions.Default, i);
|
||||||
|
|
||||||
|
if (bitmap.Buffer != IntPtr.Zero) unsafe
|
||||||
{
|
{
|
||||||
return null;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QImageBuffer image = new QImageBuffer(QImageFormat.RedU8, (int)bitmap.Width, (int)bitmap.Rows);
|
buffer.UnlockBits();
|
||||||
image.LockBits2d(out QImageLock lk, QImageLockOptions.Default);
|
|
||||||
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
Buffer.MemoryCopy((void*)bitmap.Buffer, (void*)lk.ImagePtr, lk.Width * lk.Height, bitmap.Width * bitmap.Rows);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
image.UnlockBits();
|
return new QFontPage(this, codepage, size, options, buffer, allMetrics);
|
||||||
return image;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
|
@ -4,14 +4,14 @@ using System.Collections;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Quik.Media.Font;
|
using Quik.Media;
|
||||||
|
|
||||||
// WebRequest is obsolete but runs on .NET framework.
|
// WebRequest is obsolete but runs on .NET framework.
|
||||||
#pragma warning disable SYSLIB0014
|
#pragma warning disable SYSLIB0014
|
||||||
|
|
||||||
namespace Quik.Media.Defaults
|
namespace Quik.Media.Defaults
|
||||||
{
|
{
|
||||||
public class StbMediaLoader : MediaLoader<string>, MediaLoader<Uri>, MediaLoader<FileInfo>, MediaLoader<FontFace>
|
public class StbMediaLoader : MediaLoader<string>, MediaLoader<Uri>, MediaLoader<FileInfo>, MediaLoader<FontInfo>
|
||||||
{
|
{
|
||||||
public bool AllowRemoteTransfers { get; set; } = false;
|
public bool AllowRemoteTransfers { get; set; } = false;
|
||||||
private readonly ArrayPool<byte> ByteArrays = ArrayPool<byte>.Create();
|
private readonly ArrayPool<byte> ByteArrays = ArrayPool<byte>.Create();
|
||||||
@ -31,9 +31,9 @@ namespace Quik.Media.Defaults
|
|||||||
{
|
{
|
||||||
return GetMedia((FileInfo)key, hint);
|
return GetMedia((FileInfo)key, hint);
|
||||||
}
|
}
|
||||||
else if (t == typeof(FontFace))
|
else if (t == typeof(FontInfo))
|
||||||
{
|
{
|
||||||
return GetMedia((FontFace)key, hint);
|
return GetMedia((FontInfo)key, hint);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -56,7 +56,7 @@ namespace Quik.Media.Defaults
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDisposable GetMedia(FontFace key, MediaHint hint)
|
public IDisposable GetMedia(FontInfo key, MediaHint hint)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@ -1,151 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
#if false
|
|
||||||
namespace Quik.Media.Defaults.Win32
|
|
||||||
{
|
|
||||||
|
|
||||||
public class EnumerateFonts
|
|
||||||
{
|
|
||||||
private const byte DEFAULT_CHARSET = 1;
|
|
||||||
|
|
||||||
public static void Enumerate(FontFace font)
|
|
||||||
{
|
|
||||||
/* It's windows, just borrow the desktop window. */
|
|
||||||
IntPtr hdc = GetDC(GetDesktopWindow());
|
|
||||||
|
|
||||||
List<(LogFontA, TextMetricA)> list = new List<(LogFontA, TextMetricA)>();
|
|
||||||
|
|
||||||
LogFontA font2 = new LogFontA()
|
|
||||||
{
|
|
||||||
//FaceName = font.Family,
|
|
||||||
Weight = ((font.Style & FontStyle.Bold) != 0) ? FontWeight.Bold : FontWeight.Regular,
|
|
||||||
Italic = (font.Style & FontStyle.Italic) != 0,
|
|
||||||
CharSet = DEFAULT_CHARSET
|
|
||||||
};
|
|
||||||
|
|
||||||
Console.WriteLine(font2.FaceName);
|
|
||||||
|
|
||||||
EnumFontFamiliesExProc proc = (in LogFontA font, in TextMetricA metric, int type, IntPtr lparam) =>
|
|
||||||
{
|
|
||||||
list.Add((font, metric));
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
EnumFontFamiliesExA(hdc, font2, proc, IntPtr.Zero, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private const string gdi32 = "Gdi32.dll";
|
|
||||||
private const string user32 = "User32.dll";
|
|
||||||
|
|
||||||
[DllImport(gdi32)]
|
|
||||||
private static extern int EnumFontFamiliesExA(
|
|
||||||
IntPtr hdc,
|
|
||||||
in LogFontA font,
|
|
||||||
[MarshalAs(UnmanagedType.FunctionPtr)] EnumFontFamiliesExProc proc,
|
|
||||||
IntPtr lparam,
|
|
||||||
int flags /* Should be zero. */);
|
|
||||||
|
|
||||||
[DllImport(user32)]
|
|
||||||
private static extern IntPtr /* HWND */ GetDesktopWindow();
|
|
||||||
|
|
||||||
[DllImport(user32)]
|
|
||||||
private static extern IntPtr /* HDC */ GetDC(IntPtr hwnd);
|
|
||||||
|
|
||||||
private delegate int EnumFontFamiliesExProc(in LogFontA font, in TextMetricA metric, int fontType, IntPtr lParam);
|
|
||||||
|
|
||||||
private struct LogFontA
|
|
||||||
{
|
|
||||||
public long Height;
|
|
||||||
public long Width;
|
|
||||||
public long Escapement;
|
|
||||||
public long Orientation;
|
|
||||||
public FontWeight Weight;
|
|
||||||
[MarshalAs(UnmanagedType.U1)]
|
|
||||||
public bool Italic;
|
|
||||||
[MarshalAs(UnmanagedType.U1)]
|
|
||||||
public bool Underline;
|
|
||||||
[MarshalAs(UnmanagedType.U1)]
|
|
||||||
public bool StrikeOut;
|
|
||||||
public byte CharSet;
|
|
||||||
public byte OutPrecision;
|
|
||||||
public byte ClipPrecision;
|
|
||||||
public byte PitchAndFamily;
|
|
||||||
private unsafe fixed byte aFaceName[32];
|
|
||||||
public unsafe string FaceName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
fixed (byte* str = aFaceName)
|
|
||||||
{
|
|
||||||
int len = 0;
|
|
||||||
for (; str[len] != 0 && len < 32; len++) ;
|
|
||||||
return Encoding.UTF8.GetString(str, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
fixed (byte *str = aFaceName)
|
|
||||||
{
|
|
||||||
Span<byte> span = new Span<byte>(str, 32);
|
|
||||||
Encoding.UTF8.GetBytes(value, span);
|
|
||||||
span[31] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct TextMetricA
|
|
||||||
{
|
|
||||||
public long Height;
|
|
||||||
public long Ascent;
|
|
||||||
public long Descent;
|
|
||||||
public long InternalLeading;
|
|
||||||
public long ExternalLeading;
|
|
||||||
public long AveCharWidth;
|
|
||||||
public long MaxCharWidth;
|
|
||||||
public long Weight;
|
|
||||||
public long Overhang;
|
|
||||||
public long DigitizedAspectX;
|
|
||||||
public long DigitizedAspectY;
|
|
||||||
public byte FirstChar;
|
|
||||||
public byte LastChar;
|
|
||||||
public byte DefaultChar;
|
|
||||||
public byte BreakChar;
|
|
||||||
public byte Italic;
|
|
||||||
public byte Underlined;
|
|
||||||
public byte StruckOut;
|
|
||||||
public byte PitchAndFamily;
|
|
||||||
public byte CharSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum FontWeight : long
|
|
||||||
{
|
|
||||||
DontCare = 0,
|
|
||||||
Thin = 100,
|
|
||||||
ExtraLight = 200,
|
|
||||||
UltraLight = 200,
|
|
||||||
Light = 300,
|
|
||||||
Normal = 400,
|
|
||||||
Regular = 400,
|
|
||||||
Medium = 500,
|
|
||||||
Semibold = 600,
|
|
||||||
Demibold = 600,
|
|
||||||
Bold = 700,
|
|
||||||
Extrabold = 800,
|
|
||||||
Ultrabold = 800,
|
|
||||||
Heavy = 900,
|
|
||||||
Black = 900
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -87,18 +87,5 @@ namespace Quik.OpenTK
|
|||||||
public void PortShow(IQuikPortHandle port, bool shown = true) => ((OpenTKPort)port).Show(shown);
|
public void PortShow(IQuikPortHandle port, bool shown = true) => ((OpenTKPort)port).Show(shown);
|
||||||
|
|
||||||
public void PortPaint(IQuikPortHandle port, CommandList commands) => ((OpenTKPort)port).Paint(commands);
|
public void PortPaint(IQuikPortHandle port, CommandList commands) => ((OpenTKPort)port).Paint(commands);
|
||||||
|
|
||||||
public void GetMaximumImage(out int width, out int height)
|
|
||||||
{
|
|
||||||
GL.Get(GLEnum.GL_MAX_TEXTURE_SIZE, out int value);
|
|
||||||
width = height = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetMaximumImage(out int width, out int height, out int depth)
|
|
||||||
{
|
|
||||||
GetMaximumImage(out width, out height);
|
|
||||||
GL.Get(GLEnum.GL_MAX_ARRAY_TEXTURE_LAYERS, out int value);
|
|
||||||
depth = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -78,7 +78,6 @@ namespace Quik.OpenTK
|
|||||||
if (!_glDriver.IsInit)
|
if (!_glDriver.IsInit)
|
||||||
_glDriver.Init();
|
_glDriver.Init();
|
||||||
|
|
||||||
GL.Clear(GLEnum.GL_COLOR_BUFFER_BIT);
|
|
||||||
_glDriver.Draw(_vertexEngine.DrawQueue, view);
|
_glDriver.Draw(_vertexEngine.DrawQueue, view);
|
||||||
|
|
||||||
_window.Context.SwapBuffers();
|
_window.Context.SwapBuffers();
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Quik.Media.Color
|
namespace Quik.Media
|
||||||
{
|
{
|
||||||
public class QImageBuffer : QImage
|
public class QImageBuffer : QImage
|
||||||
{
|
{
|
||||||
private byte[] buffer;
|
private byte[] buffer;
|
||||||
GCHandle handle;
|
GCHandle handle;
|
||||||
private bool isSdf = false;
|
|
||||||
|
|
||||||
public override QImageFormat InternalFormat { get; }
|
public override QImageFormat InternalFormat { get; }
|
||||||
public override int Width { get; }
|
public override int Width { get; }
|
||||||
public override int Height { get; }
|
public override int Height { get; }
|
||||||
public override int Depth { get; }
|
public override int Depth { get; }
|
||||||
public override bool IsSdf => isSdf;
|
|
||||||
|
|
||||||
public QImageBuffer(QImageFormat format, int width, int height, int depth = 1)
|
public QImageBuffer(QImageFormat format, int width, int height, int depth = 1)
|
||||||
{
|
{
|
||||||
@ -22,7 +20,7 @@ namespace Quik.Media.Color
|
|||||||
Height = height;
|
Height = height;
|
||||||
Depth = depth;
|
Depth = depth;
|
||||||
|
|
||||||
buffer = new byte[width * height * depth];
|
buffer = new byte[width * height * depth * format.BytesPerPixel()];
|
||||||
}
|
}
|
||||||
~QImageBuffer()
|
~QImageBuffer()
|
||||||
{
|
{
|
||||||
@ -31,7 +29,7 @@ namespace Quik.Media.Color
|
|||||||
|
|
||||||
private QImageLock Lock()
|
private QImageLock Lock()
|
||||||
{
|
{
|
||||||
if (handle.IsAllocated) handle.Free();
|
handle.Free();
|
||||||
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||||
IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
|
IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
|
||||||
return new QImageLock(InternalFormat, Width, Height, Depth, ptr);
|
return new QImageLock(InternalFormat, Width, Height, Depth, ptr);
|
||||||
@ -39,32 +37,56 @@ namespace Quik.Media.Color
|
|||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
buffer = null;
|
|
||||||
if (handle.IsAllocated) handle.Free();
|
if (handle.IsAllocated) handle.Free();
|
||||||
|
buffer = null;
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void LockBits2d(out QImageLock imageLock, QImageLockOptions options)
|
public override void LockBits2d(out QImageLock imageLock, QImageLockOptions options)
|
||||||
{
|
{
|
||||||
imageLock = Lock();
|
if (options.Format != options.Format) throw new InvalidOperationException("This image type cannot be converted.");
|
||||||
|
if (Depth > 1) throw new InvalidOperationException("This texture has a depth component.");
|
||||||
|
|
||||||
|
UnlockBits();
|
||||||
|
|
||||||
|
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||||
|
IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
|
||||||
|
imageLock = new QImageLock(InternalFormat, Width, Height, Depth, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void LockBits3d(out QImageLock imageLock, QImageLockOptions options)
|
public override void LockBits3d(out QImageLock imageLock, QImageLockOptions options)
|
||||||
{
|
{
|
||||||
imageLock = Lock();
|
if (options.Format != options.Format) throw new InvalidOperationException("This image type cannot be converted.");
|
||||||
|
UnlockBits();
|
||||||
|
|
||||||
|
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||||
|
IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
|
||||||
|
imageLock = new QImageLock(InternalFormat, Width, Height, Depth, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void LockBits3d(out QImageLock imageLock, QImageLockOptions options, int depth)
|
public override void LockBits3d(out QImageLock imageLock, QImageLockOptions options, int depth)
|
||||||
{
|
{
|
||||||
imageLock = Lock();
|
if (options.Format != options.Format) throw new InvalidOperationException("This image type cannot be converted.");
|
||||||
|
if (depth < 0 || depth > Depth)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(depth), "Depth must be in range.");
|
||||||
|
|
||||||
|
UnlockBits();
|
||||||
|
|
||||||
|
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||||
|
IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
|
||||||
|
imageLock = new QImageLock(
|
||||||
|
InternalFormat,
|
||||||
|
Width,
|
||||||
|
Height,
|
||||||
|
1,
|
||||||
|
ptr + (depth * Width * Height * InternalFormat.BytesPerPixel()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UnlockBits()
|
public override void UnlockBits()
|
||||||
{
|
{
|
||||||
|
if (handle.IsAllocated)
|
||||||
handle.Free();
|
handle.Free();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSdf(bool value = true) => isSdf = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,9 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Reflection;
|
|
||||||
using Quik.Media.Color;
|
|
||||||
|
|
||||||
namespace Quik.Media.Font
|
namespace Quik.Media.Font
|
||||||
{
|
{
|
||||||
@ -21,28 +17,11 @@ namespace Quik.Media.Font
|
|||||||
private readonly Dictionary<int, FontAtlasGlyphInfo> glyphs = new Dictionary<int, FontAtlasGlyphInfo>();
|
private readonly Dictionary<int, FontAtlasGlyphInfo> glyphs = new Dictionary<int, FontAtlasGlyphInfo>();
|
||||||
private int index = 0;
|
private int index = 0;
|
||||||
private AtlasPage last = null;
|
private AtlasPage last = null;
|
||||||
private bool isSdf = false;
|
|
||||||
private int expansion;
|
|
||||||
|
|
||||||
public bool IsSdf
|
public FontAtlas(int width, int height)
|
||||||
{
|
|
||||||
get => isSdf;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
foreach (AtlasPage page in atlases)
|
|
||||||
{
|
|
||||||
(page.Image as QImageBuffer).SetSdf(value);
|
|
||||||
}
|
|
||||||
isSdf = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FontAtlas(int width, int height, bool isSdf, int expansion = 4)
|
|
||||||
{
|
{
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
IsSdf = isSdf;
|
|
||||||
this.expansion = expansion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool GetGlyph(int codepoint, out FontAtlasGlyphInfo info)
|
public bool GetGlyph(int codepoint, out FontAtlasGlyphInfo info)
|
||||||
@ -54,7 +33,7 @@ namespace Quik.Media.Font
|
|||||||
{
|
{
|
||||||
info = new FontAtlasGlyphInfo() { Codepoint = codepoint };
|
info = new FontAtlasGlyphInfo() { Codepoint = codepoint };
|
||||||
|
|
||||||
if (last == null || !last.WouldFit(source))
|
if (last == null || last.IsFull)
|
||||||
{
|
{
|
||||||
AddPage();
|
AddPage();
|
||||||
}
|
}
|
||||||
@ -72,8 +51,7 @@ namespace Quik.Media.Font
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
last = new AtlasPage(width, height, expansion);
|
last = new AtlasPage(width, height);
|
||||||
(last.Image as QImageBuffer).SetSdf(IsSdf);
|
|
||||||
atlases.Add(last);
|
atlases.Add(last);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,14 +92,12 @@ namespace Quik.Media.Font
|
|||||||
public QImage Image;
|
public QImage Image;
|
||||||
public int PointerX, PointerY;
|
public int PointerX, PointerY;
|
||||||
public int RowHeight;
|
public int RowHeight;
|
||||||
public int Expansion;
|
|
||||||
|
|
||||||
public bool IsFull => PointerX > Image.Width || PointerY > Image.Height;
|
public bool IsFull => PointerX > Image.Width || PointerY > Image.Height;
|
||||||
|
|
||||||
public AtlasPage(int width, int height, int expansion)
|
public AtlasPage(int width, int height)
|
||||||
{
|
{
|
||||||
Image = new QImageBuffer(QImageFormat.RedU8, width, height);
|
Image = new QImageBuffer(QImageFormat.RedU8, width, height);
|
||||||
Expansion = expansion;
|
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,8 +110,8 @@ namespace Quik.Media.Font
|
|||||||
src.CopyTo(dst, PointerX, PointerY);
|
src.CopyTo(dst, PointerX, PointerY);
|
||||||
Image.UnlockBits();
|
Image.UnlockBits();
|
||||||
|
|
||||||
QVec2 min = new QVec2((float)PointerX/Image.Width, (float)PointerY/Image.Height);
|
QVec2 min = new QVec2(PointerX/Image.Width, PointerY/Image.Width);
|
||||||
QVec2 size = new QVec2((float)src.Width/Image.Width, (float)src.Height/Image.Height);
|
QVec2 size = new QVec2(src.Width/Image.Width, src.Height/Image.Height);
|
||||||
|
|
||||||
prototype.Image = Image;
|
prototype.Image = Image;
|
||||||
prototype.UVs = new QRectangle(min + size, min);
|
prototype.UVs = new QRectangle(min + size, min);
|
||||||
@ -151,7 +127,7 @@ namespace Quik.Media.Font
|
|||||||
public void AdvanceRow()
|
public void AdvanceRow()
|
||||||
{
|
{
|
||||||
PointerX = 0;
|
PointerX = 0;
|
||||||
PointerY += RowHeight + Expansion;
|
PointerY += RowHeight;
|
||||||
|
|
||||||
RowHeight = 0;
|
RowHeight = 0;
|
||||||
}
|
}
|
||||||
@ -159,7 +135,7 @@ namespace Quik.Media.Font
|
|||||||
public void AdvanceColumn(int width, int height)
|
public void AdvanceColumn(int width, int height)
|
||||||
{
|
{
|
||||||
RowHeight = Math.Max(RowHeight, height);
|
RowHeight = Math.Max(RowHeight, height);
|
||||||
PointerX += width + Expansion;
|
PointerX += width;
|
||||||
|
|
||||||
if (PointerX > Image.Width)
|
if (PointerX > Image.Width)
|
||||||
{
|
{
|
||||||
@ -177,11 +153,6 @@ namespace Quik.Media.Font
|
|||||||
|
|
||||||
isDisposed = true;
|
isDisposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool WouldFit(QImageLock source)
|
|
||||||
{
|
|
||||||
return !IsFull || PointerX + source.Width > Image.Width || PointerY + source.Height > Image.Height;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
66
Quik/Media/ImageBuffer.cs
Normal file
66
Quik/Media/ImageBuffer.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Quik.Media.Color
|
||||||
|
{
|
||||||
|
public class QImageBuffer : QImage
|
||||||
|
{
|
||||||
|
private byte[] buffer;
|
||||||
|
GCHandle handle;
|
||||||
|
|
||||||
|
public override QImageFormat InternalFormat { get; }
|
||||||
|
public override int Width { get; }
|
||||||
|
public override int Height { get; }
|
||||||
|
public override int Depth { get; }
|
||||||
|
|
||||||
|
public QImageBuffer(QImageFormat format, int width, int height, int depth = 1)
|
||||||
|
{
|
||||||
|
InternalFormat = format;
|
||||||
|
Width = width;
|
||||||
|
Height = height;
|
||||||
|
Depth = depth;
|
||||||
|
|
||||||
|
buffer = new byte[width * height * depth];
|
||||||
|
}
|
||||||
|
~QImageBuffer()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private QImageLock Lock()
|
||||||
|
{
|
||||||
|
handle.Free();
|
||||||
|
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||||
|
IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
|
||||||
|
return new QImageLock(InternalFormat, Width, Height, Depth, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
buffer = null;
|
||||||
|
handle.Free();
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void LockBits2d(out QImageLock imageLock, QImageLockOptions options)
|
||||||
|
{
|
||||||
|
imageLock = Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void LockBits3d(out QImageLock imageLock, QImageLockOptions options)
|
||||||
|
{
|
||||||
|
imageLock = Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void LockBits3d(out QImageLock imageLock, QImageLockOptions options, int depth)
|
||||||
|
{
|
||||||
|
imageLock = Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UnlockBits()
|
||||||
|
{
|
||||||
|
handle.Free();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Quik.Media;
|
using Quik.Media;
|
||||||
using Quik.Media.Font;
|
using Quik.Media.Font;
|
||||||
|
|
||||||
@ -18,23 +17,8 @@ namespace Quik.Media
|
|||||||
public FontStretch Stretch => Face.Stretch;
|
public FontStretch Stretch => Face.Stretch;
|
||||||
|
|
||||||
public abstract bool HasRune(int rune);
|
public abstract bool HasRune(int rune);
|
||||||
protected abstract QImage Render(out QGlyphMetrics metrics, int codepoint, float size, in FontRasterizerOptions options);
|
public abstract QFontPage RasterizePage(int codepage, float size, in FontRasterizerOptions options);
|
||||||
|
public QFontPage RasterizePage(int codepage, float size) => RasterizePage(codepage, size, FontRasterizerOptions.Default);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// IDisposable
|
// IDisposable
|
||||||
private bool isDisposed = false;
|
private bool isDisposed = false;
|
||||||
@ -48,71 +32,6 @@ namespace Quik.Media
|
|||||||
}
|
}
|
||||||
protected virtual void Dispose(bool disposing) { }
|
protected virtual void Dispose(bool disposing) { }
|
||||||
public void Dispose() => DisposePrivate(true);
|
public void Dispose() => DisposePrivate(true);
|
||||||
|
|
||||||
private class SizedFontCollection
|
|
||||||
{
|
|
||||||
public float Size { get; }
|
|
||||||
private readonly Dictionary<int, FontGlyph> glyphs = new Dictionary<int, FontGlyph>();
|
|
||||||
private readonly FontAtlas atlas;
|
|
||||||
|
|
||||||
public SizedFontCollection(float size)
|
|
||||||
{
|
|
||||||
Size = size;
|
|
||||||
|
|
||||||
QuikApplication.Current.Platform.GetMaximumImage(out int height, out int width);
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
CodePoint = codepoint;
|
|
||||||
Image = image;
|
|
||||||
Metrics = metrics;
|
|
||||||
UVs = uvs;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct FontRasterizerOptions
|
public struct FontRasterizerOptions
|
||||||
@ -123,7 +42,53 @@ namespace Quik.Media
|
|||||||
public static readonly FontRasterizerOptions Default = new FontRasterizerOptions()
|
public static readonly FontRasterizerOptions Default = new FontRasterizerOptions()
|
||||||
{
|
{
|
||||||
Resolution = 96.0f,
|
Resolution = 96.0f,
|
||||||
Sdf = false
|
Sdf = true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class QFontPage : IDisposable
|
||||||
|
{
|
||||||
|
public QFont Font { get; }
|
||||||
|
public int CodePage { get; }
|
||||||
|
public float Size { get; }
|
||||||
|
public virtual QImage Image { get; } = null;
|
||||||
|
public virtual QGlyphMetrics[] Metrics { get; } = Array.Empty<QGlyphMetrics>();
|
||||||
|
|
||||||
|
public FontRasterizerOptions Options { get; }
|
||||||
|
public float Resolution => Options.Resolution;
|
||||||
|
public bool Sdf => Options.Sdf;
|
||||||
|
|
||||||
|
public void Dispose() => DisposeInternal(false);
|
||||||
|
|
||||||
|
protected QFontPage(QFont font, int codepage, float size, in FontRasterizerOptions options)
|
||||||
|
{
|
||||||
|
Font = font;
|
||||||
|
CodePage = codepage;
|
||||||
|
Size = size;
|
||||||
|
Options = options;
|
||||||
|
}
|
||||||
|
public QFontPage(QFont font, int codepage, float size, in FontRasterizerOptions options, QImage image, QGlyphMetrics[] metrics)
|
||||||
|
: this(font, codepage, size, options)
|
||||||
|
{
|
||||||
|
Image = image;
|
||||||
|
Metrics = metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool isDisposed = false;
|
||||||
|
private void DisposeInternal(bool disposing)
|
||||||
|
{
|
||||||
|
if (isDisposed) return;
|
||||||
|
|
||||||
|
Dispose(disposing);
|
||||||
|
isDisposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
Image?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -78,7 +78,7 @@ namespace Quik.Media
|
|||||||
int bpp = Format.BytesPerPixel();
|
int bpp = Format.BytesPerPixel();
|
||||||
for (int i = 0; i < Height; i++)
|
for (int i = 0; i < Height; i++)
|
||||||
{
|
{
|
||||||
IntPtr srcPtr = (IntPtr)((long)ImagePtr + i * Width * bpp);
|
IntPtr srcPtr = (IntPtr)((long)ImagePtr + i * bpp);
|
||||||
|
|
||||||
long dstPos = x + i * destination.Width;
|
long dstPos = x + i * destination.Width;
|
||||||
IntPtr dstPtr = (IntPtr)((long)destination.ImagePtr + dstPos * bpp);
|
IntPtr dstPtr = (IntPtr)((long)destination.ImagePtr + dstPos * bpp);
|
||||||
|
@ -5,7 +5,6 @@ using Quik.VertexGenerator;
|
|||||||
using static Quik.OpenGL.GLEnum;
|
using static Quik.OpenGL.GLEnum;
|
||||||
using Quik.Media;
|
using Quik.Media;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Quik.OpenGL
|
namespace Quik.OpenGL
|
||||||
{
|
{
|
||||||
@ -106,7 +105,7 @@ namespace Quik.OpenGL
|
|||||||
GL.UseProgram(program);
|
GL.UseProgram(program);
|
||||||
GL.Uniform1(fMaxZ, (float)(queue.ZDepth+1));
|
GL.Uniform1(fMaxZ, (float)(queue.ZDepth+1));
|
||||||
GL.UniformMatrix4(m4Transforms, false, in matrix);
|
GL.UniformMatrix4(m4Transforms, false, in matrix);
|
||||||
GL.Uniform1(fSdfThreshold, 0.5f);
|
GL.Uniform1(fSdfThreshold, 0.0f);
|
||||||
GL.Uniform1(tx2d, 0);
|
GL.Uniform1(tx2d, 0);
|
||||||
GL.Enable(GL_BLEND);
|
GL.Enable(GL_BLEND);
|
||||||
GL.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
GL.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
@ -11,9 +11,6 @@ namespace Quik.OpenGL
|
|||||||
GL_RENDERER = 0x1F01,
|
GL_RENDERER = 0x1F01,
|
||||||
GL_VERSION = 0x1F02,
|
GL_VERSION = 0x1F02,
|
||||||
GL_EXTENSIONS = 0x1F03,
|
GL_EXTENSIONS = 0x1F03,
|
||||||
GL_MAX_TEXTURE_SIZE = 0x0D33,
|
|
||||||
GL_MAX_3D_TEXTURE_SIZE = 0x8073,
|
|
||||||
GL_MAX_ARRAY_TEXTURE_LAYERS = 0x88FF,
|
|
||||||
|
|
||||||
GL_MULTISAMPLE = 0x809D,
|
GL_MULTISAMPLE = 0x809D,
|
||||||
GL_BLEND = 0x0BE2,
|
GL_BLEND = 0x0BE2,
|
||||||
|
@ -55,7 +55,5 @@ namespace Quik.PAL
|
|||||||
void PortFocus(IQuikPortHandle port);
|
void PortFocus(IQuikPortHandle port);
|
||||||
void PortShow(IQuikPortHandle port, bool shown = true);
|
void PortShow(IQuikPortHandle port, bool shown = true);
|
||||||
void PortPaint(IQuikPortHandle port, CommandList commands);
|
void PortPaint(IQuikPortHandle port, CommandList commands);
|
||||||
void GetMaximumImage(out int width, out int height);
|
|
||||||
void GetMaximumImage(out int width, out int height, out int depth);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,71 +0,0 @@
|
|||||||
using Quik.Media;
|
|
||||||
using Quik.Media.Font;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Quik.Typography
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The font provider is a caching object that provides fonts for typesetting classes.
|
|
||||||
/// </summary>
|
|
||||||
public class FontProvider : IDisposable
|
|
||||||
{
|
|
||||||
private Dictionary<FontFace, QFont> Fonts { get; } = new Dictionary<FontFace, QFont>();
|
|
||||||
private HashSet<QFont> UsedFonts { get; } = new HashSet<QFont>();
|
|
||||||
public readonly FontRasterizerOptions RasterizerOptions;
|
|
||||||
private readonly QuikApplication App;
|
|
||||||
|
|
||||||
public QFont this[FontFace info]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (!Fonts.TryGetValue(info, out QFont font))
|
|
||||||
{
|
|
||||||
font = (QFont)App.GetMedia(info, MediaHint.Font);
|
|
||||||
Fonts[info] = font;
|
|
||||||
}
|
|
||||||
|
|
||||||
UsedFonts.Add(font);
|
|
||||||
return font;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FontProvider(QuikApplication app, in FontRasterizerOptions options)
|
|
||||||
{
|
|
||||||
RasterizerOptions = options;
|
|
||||||
App = app;
|
|
||||||
}
|
|
||||||
public FontProvider(QuikApplication app)
|
|
||||||
: this(app, FontRasterizerOptions.Default)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tracks the use of fonts used by this typesetter and removes any that haven't been referenced since the last cycle.
|
|
||||||
/// </summary>
|
|
||||||
public void Collect()
|
|
||||||
{
|
|
||||||
// foreach (FontJar jar in Fonts.Values.ToArray())
|
|
||||||
// {
|
|
||||||
// if (!UsedFonts.Contains(jar))
|
|
||||||
// {
|
|
||||||
// Fonts.Remove(jar.Info);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// UsedFonts.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool isDisposed = false;
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (isDisposed) return;
|
|
||||||
|
|
||||||
isDisposed = true;
|
|
||||||
foreach (QFont font in Fonts.Values)
|
|
||||||
{
|
|
||||||
font.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -226,7 +226,7 @@ namespace Quik.Typography
|
|||||||
|
|
||||||
pen.Y -= PostSpace;
|
pen.Y -= PostSpace;
|
||||||
|
|
||||||
group.BoundingBox = new QRectangle(width, pen.Y, 0, 0);
|
group.BoundingBox = new QRectangle(width, 0, 0, pen.Y);
|
||||||
group.Translate(-pen);
|
group.Translate(-pen);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,13 +353,13 @@ namespace Quik.Typography
|
|||||||
public struct TypesetCharacter
|
public struct TypesetCharacter
|
||||||
{
|
{
|
||||||
public int Character;
|
public int Character;
|
||||||
public QImage Texture;
|
public QuikTexture Texture;
|
||||||
public QRectangle Position;
|
public QRectangle Position;
|
||||||
public QRectangle UV;
|
public QRectangle UV;
|
||||||
|
|
||||||
public TypesetCharacter(
|
public TypesetCharacter(
|
||||||
int chr,
|
int chr,
|
||||||
QImage texture,
|
QuikTexture texture,
|
||||||
in QRectangle position,
|
in QRectangle position,
|
||||||
in QRectangle uv)
|
in QRectangle uv)
|
||||||
{
|
{
|
||||||
|
@ -1,155 +0,0 @@
|
|||||||
using Quik.CommandMachine;
|
|
||||||
using Quik.Media;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Quik.Typography
|
|
||||||
{
|
|
||||||
public static class Typesetter
|
|
||||||
{
|
|
||||||
private ref struct LineEnumerator
|
|
||||||
{
|
|
||||||
private ReadOnlySpan<char> Entire, Segment;
|
|
||||||
private bool Final;
|
|
||||||
|
|
||||||
public ReadOnlySpan<char> Current => Segment;
|
|
||||||
|
|
||||||
public LineEnumerator(ReadOnlySpan<char> value)
|
|
||||||
{
|
|
||||||
Entire = value;
|
|
||||||
Segment = ReadOnlySpan<char>.Empty;
|
|
||||||
Final = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
Segment = ReadOnlySpan<char>.Empty;
|
|
||||||
Final = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
if (Final)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (Segment == ReadOnlySpan<char>.Empty)
|
|
||||||
{
|
|
||||||
int index = Entire.IndexOf('\n');
|
|
||||||
if (index == -1)
|
|
||||||
{
|
|
||||||
Segment = Entire;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Segment = Entire.Slice(0, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Entire.Overlaps(Segment, out int offset);
|
|
||||||
|
|
||||||
if (offset + Segment.Length >= Entire.Length)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadOnlySpan<char> rest = Entire.Slice(offset + Segment.Length + 1);
|
|
||||||
|
|
||||||
int index = rest.IndexOf('\n');
|
|
||||||
if (index == -1)
|
|
||||||
{
|
|
||||||
Segment = rest;
|
|
||||||
Final = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Segment = rest.Slice(0, index);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void TypesetHorizontalDirect(this CommandList list, string str, QVec2 origin, float size, QFont font)
|
|
||||||
{
|
|
||||||
Dictionary<QImage, FontDrawInfo> drawInfo = new Dictionary<QImage, FontDrawInfo>();
|
|
||||||
var enumerator = new LineEnumerator(str.AsSpan());
|
|
||||||
|
|
||||||
QVec2 pen = origin;
|
|
||||||
while (enumerator.MoveNext())
|
|
||||||
{
|
|
||||||
ReadOnlySpan<char> line = enumerator.Current;
|
|
||||||
float rise = 0.0f;
|
|
||||||
float fall = 0.0f;
|
|
||||||
|
|
||||||
// Find out all the code pages required, and the line height.
|
|
||||||
foreach (Rune r in line.EnumerateRunes())
|
|
||||||
{
|
|
||||||
int codepoint = r.Value;
|
|
||||||
font.Get(codepoint, size, out FontGlyph glyph);
|
|
||||||
float crise = glyph.Metrics.HorizontalBearing.Y;
|
|
||||||
float cfall = glyph.Metrics.Size.Y - crise;
|
|
||||||
|
|
||||||
rise = Math.Max(crise, rise);
|
|
||||||
fall = Math.Max(cfall, fall);
|
|
||||||
}
|
|
||||||
|
|
||||||
pen += new QVec2(0, rise);
|
|
||||||
|
|
||||||
foreach (Rune r in line.EnumerateRunes())
|
|
||||||
{
|
|
||||||
FontDrawInfo info;
|
|
||||||
int codepoint = r.Value;
|
|
||||||
|
|
||||||
font.Get(codepoint, size, out FontGlyph glyph);
|
|
||||||
ref readonly QGlyphMetrics metrics = ref glyph.Metrics;
|
|
||||||
QImage image = glyph.Image;
|
|
||||||
|
|
||||||
if (image == null)
|
|
||||||
{
|
|
||||||
pen += new QVec2(metrics.Advance.X, 0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!drawInfo.TryGetValue(image, out info))
|
|
||||||
{
|
|
||||||
info = new FontDrawInfo();
|
|
||||||
info.Image = image;
|
|
||||||
info.rectangles = new List<QRectangle>();
|
|
||||||
drawInfo[image] = info;
|
|
||||||
}
|
|
||||||
|
|
||||||
QRectangle dest = new QRectangle(
|
|
||||||
pen + new QVec2(metrics.HorizontalBearing.X + metrics.Size.X, metrics.Size.Y - metrics.HorizontalBearing.Y),
|
|
||||||
pen + new QVec2(metrics.HorizontalBearing.X, -metrics.HorizontalBearing.Y));
|
|
||||||
|
|
||||||
info.rectangles.Add(dest);
|
|
||||||
info.rectangles.Add(glyph.UVs);
|
|
||||||
|
|
||||||
pen.X += metrics.Advance.X;
|
|
||||||
}
|
|
||||||
|
|
||||||
pen.X = origin.X;
|
|
||||||
pen.Y += fall;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now for each rectangle we can dispatch draw calls.
|
|
||||||
foreach (FontDrawInfo info in drawInfo.Values)
|
|
||||||
{
|
|
||||||
list.Image(info.Image, info.rectangles.ToArray(), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct FontDrawInfo
|
|
||||||
{
|
|
||||||
public QImage Image;
|
|
||||||
public List<QRectangle> rectangles;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1063,7 +1063,7 @@ namespace Quik.VertexGenerator
|
|||||||
DrawQueue.AddVertex(vertex);
|
DrawQueue.AddVertex(vertex);
|
||||||
|
|
||||||
vertex.Position = new QVec2(rect.Right, rect.Top);
|
vertex.Position = new QVec2(rect.Right, rect.Top);
|
||||||
vertex.TextureCoordinates = new QVec2(uvs.Right, uvs.Top);
|
vertex.TextureCoordinates = new QVec2(uvs.Right, uvs.Right);
|
||||||
DrawQueue.AddVertex(vertex);
|
DrawQueue.AddVertex(vertex);
|
||||||
|
|
||||||
DrawQueue.AddElement(0); DrawQueue.AddElement(2); DrawQueue.AddElement(3);
|
DrawQueue.AddElement(0); DrawQueue.AddElement(2); DrawQueue.AddElement(3);
|
||||||
|
@ -13,8 +13,9 @@ out vec4 fragColor;
|
|||||||
uniform int iEnableSdf;
|
uniform int iEnableSdf;
|
||||||
uniform int iEnableTexture;
|
uniform int iEnableTexture;
|
||||||
uniform int iAlphaDiscard;
|
uniform int iAlphaDiscard;
|
||||||
uniform float fSdfThreshold = 0.5;
|
uniform float fSdfThreshold;
|
||||||
uniform sampler2D tx2d;
|
uniform sampler2D tx2d;
|
||||||
|
uniform sampler2DArray tx2dArray;
|
||||||
|
|
||||||
const float fAlphaThreshold = 0.01;
|
const float fAlphaThreshold = 0.01;
|
||||||
|
|
||||||
@ -22,13 +23,7 @@ vec4 getTexture()
|
|||||||
{
|
{
|
||||||
if (iEnableTexture == 3)
|
if (iEnableTexture == 3)
|
||||||
{
|
{
|
||||||
// return texture(tx2dArray, vec3(fv2TexPos, ffTexLayer));
|
return texture(tx2dArray, vec3(fv2TexPos, ffTexLayer));
|
||||||
}
|
|
||||||
else if (iEnableSdf == 1)
|
|
||||||
{
|
|
||||||
vec2 texelSz = 1.0/vec2(textureSize(tx2d, 0));
|
|
||||||
vec2 txCoord2 = fv2TexPos + texelSz * (1 - mod(fv2TexPos, texelSz));
|
|
||||||
return texture(tx2d, txCoord2);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -46,7 +41,12 @@ void main(void)
|
|||||||
|
|
||||||
if (iEnableSdf != 0)
|
if (iEnableSdf != 0)
|
||||||
{
|
{
|
||||||
value = vec4(vec3(1.0), smoothstep(fSdfThreshold-0.1, fSdfThreshold+0.1, value.r));
|
float a = max(value.r, value.a);
|
||||||
|
|
||||||
|
value =
|
||||||
|
(a >= fSdfThreshold) ?
|
||||||
|
vec4(1.0,1.0,1.0,1.0) :
|
||||||
|
vec4(0.0,0.0,0.0,0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iAlphaDiscard != 0 && value.a <= fAlphaThreshold)
|
if (iAlphaDiscard != 0 && value.a <= fAlphaThreshold)
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
using System;
|
using Quik;
|
||||||
using Quik;
|
|
||||||
using Quik.CommandMachine;
|
using Quik.CommandMachine;
|
||||||
using Quik.Controls;
|
using Quik.Controls;
|
||||||
using Quik.OpenTK;
|
using Quik.OpenTK;
|
||||||
using Quik.Media.Defaults;
|
|
||||||
using Quik.Media;
|
|
||||||
using Quik.Typography;
|
|
||||||
using Quik.PAL;
|
|
||||||
|
|
||||||
namespace QuikDemo
|
namespace QuikDemo
|
||||||
{
|
{
|
||||||
@ -22,23 +18,11 @@ namespace QuikDemo
|
|||||||
|
|
||||||
public class EmptyView : View
|
public class EmptyView : View
|
||||||
{
|
{
|
||||||
private QFont font;
|
protected override void PaintBegin(CommandQueue cmd)
|
||||||
|
|
||||||
protected override void PaintBegin(CommandList cmd)
|
|
||||||
{
|
{
|
||||||
base.PaintBegin(cmd);
|
base.PaintBegin(cmd);
|
||||||
|
|
||||||
if (font == null)
|
cmd.Rectangle(new QRectangle(0, 0, 16, 16));
|
||||||
{
|
|
||||||
IFontDataBase db = FontDataBaseProvider.Instance;
|
|
||||||
font = new QFontFreeType(db.FontFileInfo(db.Sans).OpenRead());
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Rectangle(new QRectangle(16, 16, 0, 0));
|
|
||||||
cmd.TypesetHorizontalDirect(
|
|
||||||
"The quick brown fox jumps over the lazy dog.\n" +
|
|
||||||
"hi?",
|
|
||||||
new QVec2(64.33f, 0.77f), 9, font);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user