Compare commits
	
		
			2 Commits
		
	
	
		
			7cb47c721b
			...
			bd69c0d93f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| bd69c0d93f | |||
| 279e619c3b | 
@ -1,8 +1,9 @@
 | 
				
			|||||||
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
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -11,7 +12,7 @@ namespace Quik.Media.Defaults
 | 
				
			|||||||
        private MemoryStream ms;
 | 
					        private MemoryStream ms;
 | 
				
			||||||
        private FTFace face;
 | 
					        private FTFace face;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public override FontInfo Info => throw new NotImplementedException();
 | 
					        public override FontFace Face => throw new NotImplementedException();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public QFontFreeType(Stream stream)
 | 
					        public QFontFreeType(Stream stream)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@ -30,65 +31,39 @@ namespace Quik.Media.Defaults
 | 
				
			|||||||
            return FT.GetCharIndex(face, (ulong)rune) != 0;
 | 
					            return FT.GetCharIndex(face, (ulong)rune) != 0;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public override QFontPage RasterizePage(int codepage, float size, in FontRasterizerOptions options)
 | 
					        protected override QImage Render(out QGlyphMetrics metrics, int codepoint, 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];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Figure out the map size needed.
 | 
					            uint index = FT.GetCharIndex(face, (ulong)codepoint);
 | 
				
			||||||
            int pixels = 0;
 | 
					            FT.LoadGlyph(face, index, FTLoadFlags.Default);
 | 
				
			||||||
            for (int i = 0; i < 256; i++)
 | 
					
 | 
				
			||||||
 | 
					            ref readonly FTGlyphMetrics ftmetrics = ref face.Glyph.Metrics;
 | 
				
			||||||
 | 
					            metrics = new QGlyphMetrics(codepoint,
 | 
				
			||||||
 | 
					                new QVec2(ftmetrics.Width/64f, ftmetrics.Height/64f),
 | 
				
			||||||
 | 
					                new QVec2(ftmetrics.HorizontalBearingX/64f, ftmetrics.HorizontalBearingY/64f),
 | 
				
			||||||
 | 
					                new QVec2(ftmetrics.VerticalBearingX/64f, ftmetrics.VerticalBearingY/64f),
 | 
				
			||||||
 | 
					                new QVec2(ftmetrics.HorizontalAdvance/64f, ftmetrics.VerticalAdvance/64f)
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            FT.RenderGlyph(face.Glyph, options.Sdf ? FTRenderMode.Sdf : FTRenderMode.Normal);
 | 
				
			||||||
 | 
					            ref readonly FTBitmap bitmap = ref face.Glyph.Bitmap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (bitmap.Width == 0 || bitmap.Pitch == 0 || bitmap.Buffer == IntPtr.Zero)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                uint index = FT.GetCharIndex(face, (ulong)(codepage + i));
 | 
					                return null;
 | 
				
			||||||
                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);
 | 
					            QImageBuffer image = new QImageBuffer(QImageFormat.RedU8, (int)bitmap.Width, (int)bitmap.Rows);
 | 
				
			||||||
            if (1 << bits != pixels)
 | 
					            image.LockBits2d(out QImageLock lk, QImageLockOptions.Default);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            unsafe
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                pixels = 1 << bits + 1;
 | 
					                Buffer.MemoryCopy((void*)bitmap.Buffer, (void*)lk.ImagePtr, lk.Width * lk.Height, bitmap.Width * bitmap.Rows);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Now we can create a bitmap and render our glyphs.
 | 
					            image.UnlockBits();
 | 
				
			||||||
 | 
					            return image;
 | 
				
			||||||
            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)
 | 
					        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;
 | 
					using Quik.Media.Font;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 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<FontInfo>
 | 
					    public class StbMediaLoader : MediaLoader<string>, MediaLoader<Uri>, MediaLoader<FileInfo>, MediaLoader<FontFace>
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        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(FontInfo))
 | 
					            else if (t == typeof(FontFace))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return GetMedia((FontInfo)key, hint);
 | 
					                return GetMedia((FontFace)key, hint);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -56,7 +56,7 @@ namespace Quik.Media.Defaults
 | 
				
			|||||||
            throw new NotImplementedException();
 | 
					            throw new NotImplementedException();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public IDisposable GetMedia(FontInfo key, MediaHint hint)
 | 
					        public IDisposable GetMedia(FontFace key, MediaHint hint)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            throw new NotImplementedException();
 | 
					            throw new NotImplementedException();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										151
									
								
								Quik.Media.Defaults/Win32/EnumerateFonts.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								Quik.Media.Defaults/Win32/EnumerateFonts.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,151 @@
 | 
				
			|||||||
 | 
					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,5 +87,18 @@ 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,6 +78,7 @@ 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,17 +1,19 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Runtime.InteropServices;
 | 
					using System.Runtime.InteropServices;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Quik.Media
 | 
					namespace Quik.Media.Color
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    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)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@ -20,7 +22,7 @@ namespace Quik.Media
 | 
				
			|||||||
            Height = height;
 | 
					            Height = height;
 | 
				
			||||||
            Depth = depth;
 | 
					            Depth = depth;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            buffer = new byte[width * height * depth * format.BytesPerPixel()];
 | 
					            buffer = new byte[width * height * depth];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        ~QImageBuffer()
 | 
					        ~QImageBuffer()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@ -29,7 +31,7 @@ namespace Quik.Media
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private QImageLock Lock()
 | 
					        private QImageLock Lock()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            handle.Free();
 | 
					            if (handle.IsAllocated) 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);
 | 
				
			||||||
@ -37,56 +39,32 @@ namespace Quik.Media
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        protected override void Dispose(bool disposing)
 | 
					        protected override void Dispose(bool disposing)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (handle.IsAllocated) handle.Free();
 | 
					 | 
				
			||||||
            buffer = null;
 | 
					            buffer = null;
 | 
				
			||||||
 | 
					            if (handle.IsAllocated) handle.Free();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            GC.SuppressFinalize(this);
 | 
					            GC.SuppressFinalize(this);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public override void LockBits2d(out QImageLock imageLock, QImageLockOptions options)
 | 
					        public override void LockBits2d(out QImageLock imageLock, QImageLockOptions options)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (options.Format != options.Format) throw new InvalidOperationException("This image type cannot be converted.");
 | 
					            imageLock = Lock();
 | 
				
			||||||
            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)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (options.Format != options.Format) throw new InvalidOperationException("This image type cannot be converted.");
 | 
					            imageLock = Lock();
 | 
				
			||||||
            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)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (options.Format != options.Format) throw new InvalidOperationException("This image type cannot be converted.");
 | 
					            imageLock = Lock();
 | 
				
			||||||
            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,5 +1,9 @@
 | 
				
			|||||||
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
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -17,11 +21,28 @@ 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 FontAtlas(int width, int height)
 | 
					        public bool IsSdf
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            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)
 | 
				
			||||||
@ -33,7 +54,7 @@ namespace Quik.Media.Font
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            info = new FontAtlasGlyphInfo() { Codepoint = codepoint };
 | 
					            info = new FontAtlasGlyphInfo() { Codepoint = codepoint };
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            if (last == null || last.IsFull)
 | 
					            if (last == null || !last.WouldFit(source))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                AddPage();
 | 
					                AddPage();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -51,7 +72,8 @@ namespace Quik.Media.Font
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                last = new AtlasPage(width, height);
 | 
					                last = new AtlasPage(width, height, expansion);
 | 
				
			||||||
 | 
					                (last.Image as QImageBuffer).SetSdf(IsSdf);
 | 
				
			||||||
                atlases.Add(last);
 | 
					                atlases.Add(last);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -92,12 +114,14 @@ 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)
 | 
					            public AtlasPage(int width, int height, int expansion)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Image = new QImageBuffer(QImageFormat.RedU8, width, height);
 | 
					                Image = new QImageBuffer(QImageFormat.RedU8, width, height);
 | 
				
			||||||
 | 
					                Expansion = expansion;
 | 
				
			||||||
                Reset();
 | 
					                Reset();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -110,8 +134,8 @@ namespace Quik.Media.Font
 | 
				
			|||||||
                src.CopyTo(dst, PointerX, PointerY);
 | 
					                src.CopyTo(dst, PointerX, PointerY);
 | 
				
			||||||
                Image.UnlockBits();
 | 
					                Image.UnlockBits();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                QVec2 min = new QVec2(PointerX/Image.Width, PointerY/Image.Width);
 | 
					                QVec2 min = new QVec2((float)PointerX/Image.Width, (float)PointerY/Image.Height);
 | 
				
			||||||
                QVec2 size = new QVec2(src.Width/Image.Width, src.Height/Image.Height);
 | 
					                QVec2 size = new QVec2((float)src.Width/Image.Width, (float)src.Height/Image.Height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                prototype.Image = Image;
 | 
					                prototype.Image = Image;
 | 
				
			||||||
                prototype.UVs = new QRectangle(min + size, min);
 | 
					                prototype.UVs = new QRectangle(min + size, min);
 | 
				
			||||||
@ -127,7 +151,7 @@ namespace Quik.Media.Font
 | 
				
			|||||||
            public void AdvanceRow()
 | 
					            public void AdvanceRow()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                PointerX = 0;
 | 
					                PointerX = 0;
 | 
				
			||||||
                PointerY += RowHeight;
 | 
					                PointerY += RowHeight + Expansion;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                RowHeight = 0;
 | 
					                RowHeight = 0;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -135,7 +159,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;
 | 
					                PointerX += width + Expansion;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (PointerX > Image.Width)
 | 
					                if (PointerX > Image.Width)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@ -153,6 +177,11 @@ 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;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1,66 +0,0 @@
 | 
				
			|||||||
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,5 +1,6 @@
 | 
				
			|||||||
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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -17,8 +18,23 @@ 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);
 | 
				
			||||||
        public abstract QFontPage RasterizePage(int codepage, float size, in FontRasterizerOptions options);
 | 
					        protected abstract QImage Render(out QGlyphMetrics metrics, int codepoint, 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;
 | 
				
			||||||
@ -32,6 +48,71 @@ 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
 | 
				
			||||||
@ -42,53 +123,7 @@ namespace Quik.Media
 | 
				
			|||||||
        public static readonly FontRasterizerOptions Default = new FontRasterizerOptions()
 | 
					        public static readonly FontRasterizerOptions Default = new FontRasterizerOptions()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Resolution = 96.0f,
 | 
					            Resolution = 96.0f,
 | 
				
			||||||
            Sdf        = true
 | 
					            Sdf        = false
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    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 * bpp);
 | 
					                IntPtr srcPtr = (IntPtr)((long)ImagePtr + i * Width * 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,6 +5,7 @@ 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
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -105,7 +106,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.0f);
 | 
					            GL.Uniform1(fSdfThreshold, 0.5f);
 | 
				
			||||||
            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,6 +11,9 @@ 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,5 +55,7 @@ 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);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										71
									
								
								Quik/Typography/FontProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								Quik/Typography/FontProvider.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					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, 0, 0, pen.Y);
 | 
					            group.BoundingBox = new QRectangle(width, pen.Y, 0, 0);
 | 
				
			||||||
            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 QuikTexture Texture;
 | 
					        public QImage Texture;
 | 
				
			||||||
        public QRectangle Position;
 | 
					        public QRectangle Position;
 | 
				
			||||||
        public QRectangle UV;
 | 
					        public QRectangle UV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public TypesetCharacter(
 | 
					        public TypesetCharacter(
 | 
				
			||||||
            int chr,
 | 
					            int chr,
 | 
				
			||||||
            QuikTexture texture,
 | 
					            QImage texture,
 | 
				
			||||||
            in QRectangle position,
 | 
					            in QRectangle position,
 | 
				
			||||||
            in QRectangle uv)
 | 
					            in QRectangle uv)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										155
									
								
								Quik/Typography/Typesetter.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								Quik/Typography/Typesetter.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,155 @@
 | 
				
			|||||||
 | 
					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.Right);
 | 
					                vertex.TextureCoordinates = new QVec2(uvs.Right, uvs.Top);
 | 
				
			||||||
                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,9 +13,8 @@ out vec4 fragColor;
 | 
				
			|||||||
uniform int            iEnableSdf;
 | 
					uniform int            iEnableSdf;
 | 
				
			||||||
uniform int            iEnableTexture;
 | 
					uniform int            iEnableTexture;
 | 
				
			||||||
uniform int            iAlphaDiscard;
 | 
					uniform int            iAlphaDiscard;
 | 
				
			||||||
uniform float          fSdfThreshold;
 | 
					uniform float          fSdfThreshold = 0.5;
 | 
				
			||||||
uniform sampler2D      tx2d;
 | 
					uniform sampler2D      tx2d;
 | 
				
			||||||
uniform sampler2DArray tx2dArray;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const float fAlphaThreshold = 0.01;
 | 
					const float fAlphaThreshold = 0.01;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -23,7 +22,13 @@ 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
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@ -41,12 +46,7 @@ void main(void)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if (iEnableSdf != 0)
 | 
					        if (iEnableSdf != 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            float a = max(value.r, value.a);
 | 
					            value = vec4(vec3(1.0), smoothstep(fSdfThreshold-0.1, fSdfThreshold+0.1, value.r));
 | 
				
			||||||
 | 
					 | 
				
			||||||
            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,8 +1,12 @@
 | 
				
			|||||||
using Quik;
 | 
					using System;
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -18,11 +22,23 @@ namespace QuikDemo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public class EmptyView : View
 | 
					    public class EmptyView : View
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        protected override void PaintBegin(CommandQueue cmd)
 | 
					        private QFont font;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected override void PaintBegin(CommandList cmd)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            base.PaintBegin(cmd);
 | 
					            base.PaintBegin(cmd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            cmd.Rectangle(new QRectangle(0, 0, 16, 16));
 | 
					            if (font == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user