diff --git a/Quik.OpenTK/Class1.cs b/Quik.OpenTK/Class1.cs
deleted file mode 100644
index 4a1662c..0000000
--- a/Quik.OpenTK/Class1.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Quik.OpenTK
-{
- public class Class1
- {
-
- }
-}
diff --git a/Quik.OpenTK/OpenGLTexture.cs b/Quik.OpenTK/OpenGLTexture.cs
new file mode 100644
index 0000000..25feada
--- /dev/null
+++ b/Quik.OpenTK/OpenGLTexture.cs
@@ -0,0 +1,126 @@
+using System;
+using OpenTK.Graphics.OpenGL4;
+
+namespace Quik.OpenTK
+{
+ public class OpenGLTexture : IQuikTexture
+ {
+ public int TextureId { get; private set; }
+ private OpenGLTextureManager Manager { get; }
+
+ internal OpenGLTexture(OpenGLTextureManager manager, QuikImageFormat format, QuikVec2 size, bool mipmaps)
+ {
+ Manager = manager;
+ Mipmaps = mipmaps;
+ Width = (int)size.X;
+ Height = (int)size.Y;
+
+ TextureId = GL.GenTexture();
+
+ GL.BindTexture(TextureTarget.Texture2D, TextureId);
+
+ GL.TexParameter(
+ TextureTarget.Texture2D,
+ TextureParameterName.TextureMinFilter,
+ (int) (mipmaps ? TextureMinFilter.LinearMipmapNearest : TextureMinFilter.Linear));
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+
+ GL.TexImage2D(
+ TextureTarget.Texture2D,
+ 0,
+ (PixelInternalFormat)GetGlImageFormat(format),
+ Width, Height, 0,
+ PixelFormat.Rgba, PixelType.UnsignedByte,
+ IntPtr.Zero);
+ }
+
+ ~OpenGLTexture()
+ {
+ Dispose(false);
+ }
+
+ ///
+ public bool Equals(IQuikTexture other) =>
+ other is OpenGLTexture && ((OpenGLTexture)other).TextureId == TextureId;
+
+ private bool _isDisposed = false;
+ private void Dispose(bool disposing)
+ {
+ if (_isDisposed) return;
+ Manager?.Reclaim(this);
+ _isDisposed = true;
+ }
+
+ ///
+ public void Dispose() => Dispose(true);
+
+ ///
+ public int Width { get; }
+ ///
+ public int Height { get; }
+ ///
+ public bool Mipmaps { get; }
+
+ ///
+ public void Image(IntPtr data, QuikImageFormat format, QuikVec2 size, int level)
+ {
+ GL.BindTexture(TextureTarget.Texture2D, TextureId);
+ GL.TexSubImage2D(TextureTarget.Texture2D, level, 0, 0, Width, Height, GetGlImageFormat(format), GetGlDataFormat(format), data);
+ }
+
+ ///
+ public void SubImage(IntPtr data, QuikImageFormat format, QuikRectangle location, int level)
+ {
+ GL.BindTexture(TextureTarget.Texture2D, TextureId);
+ GL.TexSubImage2D(
+ TextureTarget.Texture2D,
+ level,
+ (int)location.Left,
+ (int)location.Bottom,
+ (int)location.Size.X,
+ (int)location.Size.Y,
+ GetGlImageFormat(format),
+ GetGlDataFormat(format),
+ data);
+ }
+
+ ///
+ public void GenerateMipMaps()
+ {
+ GL.BindTexture(TextureTarget.Texture2D, TextureId);
+ GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
+ }
+
+ private static PixelFormat GetGlImageFormat(QuikImageFormat format)
+ {
+ switch (format)
+ {
+ case QuikImageFormat.RedF: case QuikImageFormat.RedU8:
+ return PixelFormat.Red;
+ case QuikImageFormat.RgbF: case QuikImageFormat.RgbU8:
+ return PixelFormat.Rgb;
+ case QuikImageFormat.RgbaF: case QuikImageFormat.RgbaU8:
+ return PixelFormat.Rgba;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ private static PixelType GetGlDataFormat(QuikImageFormat format)
+ {
+ switch (format)
+ {
+ case QuikImageFormat.RedF:
+ case QuikImageFormat.RgbaF:
+ case QuikImageFormat.RgbF:
+ return PixelType.Float;
+ case QuikImageFormat.RedU8:
+ case QuikImageFormat.RgbaU8:
+ case QuikImageFormat.RgbU8:
+ return PixelType.UnsignedByte;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Quik.OpenTK/OpenGLTextureManager.cs b/Quik.OpenTK/OpenGLTextureManager.cs
new file mode 100644
index 0000000..5c8374e
--- /dev/null
+++ b/Quik.OpenTK/OpenGLTextureManager.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using OpenTK.Graphics.OpenGL4;
+
+namespace Quik.OpenTK
+{
+ public class OpenGLTextureManager : IQuikTextureManager
+ {
+ public QuikContext Context { get; set; }
+
+ private List _reclaimList = new List();
+
+ public IQuikTexture CreateTexture(QuikVec2 size, bool mipmaps, QuikImageFormat format)
+ {
+ return new OpenGLTexture(this, format, size, mipmaps);
+ }
+
+ internal void Reclaim(OpenGLTexture texture)
+ {
+ _reclaimList.Add(texture.TextureId);
+ }
+
+ public void Clear()
+ {
+ GL.DeleteTextures(_reclaimList.Count, _reclaimList.ToArray());
+ _reclaimList.Clear();
+ }
+ }
+}
diff --git a/Quik/IQuikTexture.cs b/Quik/IQuikTexture.cs
new file mode 100644
index 0000000..172976a
--- /dev/null
+++ b/Quik/IQuikTexture.cs
@@ -0,0 +1,48 @@
+using System;
+
+namespace Quik
+{
+ ///
+ /// Interface for texture instances.
+ ///
+ public interface IQuikTexture : IEquatable, IDisposable
+ {
+ ///
+ /// Width of the texture.
+ ///
+ int Width { get; }
+
+ ///
+ /// Height of the texture.
+ ///
+ int Height { get; }
+
+ ///
+ /// True if the texture can have mipmaps.
+ ///
+ bool Mipmaps { get; }
+
+ ///
+ /// Upload texture data.
+ ///
+ /// Pointer to data.
+ /// Color format of the data.
+ /// Size of the texture data.
+ /// Mip level.
+ void Image(IntPtr data, QuikImageFormat format, QuikVec2 size, int level);
+
+ ///
+ /// Upload texture data.
+ ///
+ /// Pointer to data.
+ /// Color format for the data.
+ /// Location of the data in the texture.
+ /// Mip level.
+ void SubImage(IntPtr data, QuikImageFormat format, QuikRectangle location, int level);
+
+ ///
+ /// Generate the mip maps for the texture.
+ ///
+ void GenerateMipMaps();
+ }
+}
\ No newline at end of file
diff --git a/Quik/IQuikTextureManager.cs b/Quik/IQuikTextureManager.cs
new file mode 100644
index 0000000..2439be6
--- /dev/null
+++ b/Quik/IQuikTextureManager.cs
@@ -0,0 +1,31 @@
+namespace Quik
+{
+ ///
+ /// Interface for QUIK texture managers.
+ ///
+ public interface IQuikTextureManager
+ {
+ ///
+ /// The context that owns the texture manager.
+ ///
+ QuikContext Context { get; set; }
+
+ ///
+ /// Create a texture.
+ ///
+ /// Size of the texture.
+ /// True in order to allow mipmaps.
+ /// The color format of the internal texture.
+ /// Handle to a texture object.
+ ///
+ /// All parameters are hints. If there is a situation where the texture does not
+ /// have mip levels, or format cannot be specified, ignore these parameters.
+ ///
+ IQuikTexture CreateTexture(QuikVec2 size, bool mipmaps, QuikImageFormat format);
+
+ ///
+ /// A function called on context clear. (useful for discarding old textures)
+ ///
+ void Clear();
+ }
+}
\ No newline at end of file
diff --git a/Quik/QuikContext.cs b/Quik/QuikContext.cs
index 6687880..d6eb4a4 100644
--- a/Quik/QuikContext.cs
+++ b/Quik/QuikContext.cs
@@ -12,6 +12,16 @@ namespace Quik
///
public QuikDraw Draw { get; } = new QuikDraw();
+ ///
+ /// The object responsible for managing textures.
+ ///
+ public IQuikTextureManager TextureManager { get; }
+
+ ///
+ /// The object responsible for managing fonts.
+ ///
+ public IQuikFontManager FontManager { get; }
+
public QuikStrokeStyle DefaultStroke { get; set; } = new QuikStrokeStyle(new QuikColor(0xaaaaaaff), 4);
public QuikFillStyle DefaultFill { get; set; } = new QuikFillStyle()
@@ -20,5 +30,22 @@ namespace Quik
};
public QuikFont DefaultFont { get; set; }
+
+ public QuikContext(IQuikTextureManager textureManager, IQuikFontManager fontManager)
+ {
+ TextureManager = textureManager;
+ FontManager = fontManager;
+ TextureManager.Context = FontManager.Context = this;
+ }
+
+ ///
+ /// Clear the context.
+ ///
+ public void Clear()
+ {
+ Draw.Clear();
+ TextureManager.Clear();
+ FontManager.Clear();
+ }
}
}
\ No newline at end of file
diff --git a/Quik/QuikDraw.cs b/Quik/QuikDraw.cs
index 7d978af..6dcd463 100644
--- a/Quik/QuikDraw.cs
+++ b/Quik/QuikDraw.cs
@@ -14,6 +14,8 @@ namespace Quik
///
public Queue Commands { get; } = new Queue();
+ public void Clear() => Commands.Clear();
+
public void Mask(QuikRectangle bounds) => Commands.Enqueue(new QuikCommandMask(bounds));
public void Line(QuikLine line) => Commands.Enqueue(new QuikCommandLine(line));
public void Line(params QuikLine[] lines) => Commands.Enqueue(new QuikCommandLines(lines));
diff --git a/Quik/QuikImageFormat.cs b/Quik/QuikImageFormat.cs
new file mode 100644
index 0000000..986b232
--- /dev/null
+++ b/Quik/QuikImageFormat.cs
@@ -0,0 +1,12 @@
+namespace Quik
+{
+ public enum QuikImageFormat
+ {
+ RedU8,
+ RgbU8,
+ RgbaU8,
+ RedF,
+ RgbF,
+ RgbaF
+ }
+}
\ No newline at end of file
diff --git a/Quik/Typography/IQuikFontManager.cs b/Quik/Typography/IQuikFontManager.cs
new file mode 100644
index 0000000..feac801
--- /dev/null
+++ b/Quik/Typography/IQuikFontManager.cs
@@ -0,0 +1,22 @@
+namespace Quik.Typography
+{
+ public interface IQuikFontManager
+ {
+ ///
+ /// The context owning the font manager.
+ ///
+ QuikContext Context { get; set; }
+
+ ///
+ /// Function called on clear.
+ ///
+ void Clear();
+
+ ///
+ /// Get a font object for the given font.
+ ///
+ /// The font style to fetch.
+ /// The font.
+ QuikFont GetFont(QuikFontStyle fontStyle);
+ }
+}
\ No newline at end of file
diff --git a/Quik/Typography/QuikFont.cs b/Quik/Typography/QuikFont.cs
index e13b751..18baf64 100644
--- a/Quik/Typography/QuikFont.cs
+++ b/Quik/Typography/QuikFont.cs
@@ -9,6 +9,6 @@ namespace Quik.Typography
public QuikFontStyle FontStyle => Style;
public abstract bool HasCharacter(int character);
- public abstract void GetCharacter(int character, out int texture, out QuikGlyph glyph);
+ public abstract void GetCharacter(int character, out IQuikTexture texture, out QuikGlyph glyph);
}
}
\ No newline at end of file
diff --git a/Quik/VertexGenerator/QuikVertexGenerator.cs b/Quik/VertexGenerator/QuikVertexGenerator.cs
index 8cd88a9..70cbf86 100644
--- a/Quik/VertexGenerator/QuikVertexGenerator.cs
+++ b/Quik/VertexGenerator/QuikVertexGenerator.cs
@@ -1407,7 +1407,7 @@ namespace Quik.VertexGenerator
private void RenderCharacter(QuikCommandPutChar chr)
{
- Context.DefaultFont.GetCharacter(chr.Character, out int texture, out QuikGlyph metrics);
+ Context.DefaultFont.GetCharacter(chr.Character, out IQuikTexture texture, out QuikGlyph metrics);
QuikVertex a, b, c, d;
a = b = c = d = new QuikVertex() {Color = new QuikColor(0xffffffff)};
@@ -1439,19 +1439,19 @@ namespace Quik.VertexGenerator
{
short startElement = (short)_elementBufferPointer;
QuikFont font = Context.DefaultFont;
- QuikVertex vertex = new QuikVertex() {Color = new QuikColor(0xff7777ff)};
+ QuikVertex vertex = new QuikVertex() {Color = new QuikColor(0x000000ff)};
QuikVec2 pointer = text.Position;
- int texture = -1;
+ IQuikTexture texture = null;
for (int i = 0; i < text.Text.Length; i++)
{
int chr = text.Text[i];
QuikGlyph metrics;
- int ntex;
+ IQuikTexture ntex;
font.GetCharacter(chr, out ntex, out metrics);
- if (ntex != texture && texture != -1)
+ if (ntex != texture && texture != null)
{
QuikDrawCall call = CallTemplate;
call.Texture = texture;
@@ -1510,6 +1510,6 @@ namespace Quik.VertexGenerator
public short Count;
public QuikRectangle Bounds;
public bool ClearStencil;
- public int Texture;
+ public IQuikTexture Texture;
}
}
\ No newline at end of file
diff --git a/QuikTestApplication/Program.cs b/QuikTestApplication/Program.cs
index 44f1148..d47a8d2 100644
--- a/QuikTestApplication/Program.cs
+++ b/QuikTestApplication/Program.cs
@@ -7,6 +7,8 @@ using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;
using OpenTK.Windowing.GraphicsLibraryFramework;
+using Quik.OpenTK;
+using Quik.Typography;
namespace QuikTestApplication
{
@@ -52,7 +54,7 @@ void main()
window.Context.MakeCurrent();
GL.LoadBindings(new GLFWBindingsContext());
- QuikContext context = new QuikContext();
+ QuikContext context = new QuikContext(new OpenGLTextureManager(), new TextFontManager());
QuikVertexGenerator gen = new QuikVertexGenerator(context);
GL.Enable(EnableCap.Multisample);
@@ -143,20 +145,7 @@ void main()
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Nearest);
- TestFont font = new TestFont();
- font.TextureBase = GL.GenTexture();
- GL.BindTexture(TextureTarget.Texture2D, font.TextureBase);
- GL.TexImage2D(
- TextureTarget.Texture2D,
- 0,
- PixelInternalFormat.Rgb,
- (int)TestFont.TextureSize.X, (int)TestFont.TextureSize.Y, 0,
- PixelFormat.Rgba, PixelType.UnsignedByte,
- TestFont.Texture);
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Nearest);
-
- context.DefaultFont = font;
+ context.DefaultFont = context.FontManager.GetFont(null);
window.Context.SwapInterval = 0;
@@ -168,6 +157,8 @@ void main()
GL.ClearColor(1,1,1,1);
GL.Clear(ClearBufferMask.ColorBufferBit);
+ GL.Enable(EnableCap.Blend);
+ GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
Matrix4 matrix = Matrix4.CreateOrthographicOffCenter(
0,
@@ -239,7 +230,9 @@ void main()
foreach (QuikDrawCall call in gen.DrawCalls)
{
- GL.BindTexture(TextureTarget.Texture2D, call.Texture == 0 ? whiteTexture : call.Texture);
+ GL.BindTexture(
+ TextureTarget.Texture2D,
+ call.Texture == null ? whiteTexture : (call.Texture as OpenGLTexture).TextureId);
GL.DrawElements(BeginMode.Triangles, call.Count, DrawElementsType.UnsignedShort, call.Offset);
}
@@ -254,5 +247,26 @@ void main()
window.Context.SwapBuffers();
}
}
+
+ public class TextFontManager : IQuikFontManager
+ {
+ public QuikContext Context { get; set; }
+
+ private TestFont _font;
+
+ public void Clear()
+ {
+ }
+
+ public QuikFont GetFont(QuikFontStyle fontStyle)
+ {
+ if (_font is null)
+ {
+ _font = new TestFont(Context);
+ }
+
+ return _font;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/QuikTestApplication/QuikTestApplication.csproj b/QuikTestApplication/QuikTestApplication.csproj
index b4f36c4..0c7591c 100644
--- a/QuikTestApplication/QuikTestApplication.csproj
+++ b/QuikTestApplication/QuikTestApplication.csproj
@@ -3,6 +3,7 @@
Exe
net6.0
+ true
diff --git a/QuikTestApplication/TestFont.cs b/QuikTestApplication/TestFont.cs
index 66977ee..e2f051b 100644
--- a/QuikTestApplication/TestFont.cs
+++ b/QuikTestApplication/TestFont.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Xml;
using Quik;
using Quik.Typography;
@@ -15,10 +17,22 @@ namespace QuikTestApplication
private static HashSet _characters = new HashSet();
private static Dictionary _glyphs = new Dictionary();
- public static byte[] Texture { get; private set; } = Array.Empty();
+ public static byte[] TextureData { get; private set; } = Array.Empty();
public static QuikVec2 TextureSize { get; private set; }
- public int TextureBase { get; set; } = 1;
+ public IQuikTexture Texture { get; }
+
+ public TestFont(QuikContext context)
+ {
+ Texture = context.TextureManager.CreateTexture(TextureSize, false, QuikImageFormat.RgbaU8);
+ unsafe
+ {
+ fixed (byte* ptr = &TextureData[0])
+ {
+ Texture.Image((IntPtr)ptr, QuikImageFormat.RgbaU8, TextureSize, 0);
+ }
+ }
+ }
static TestFont()
{
@@ -75,8 +89,18 @@ namespace QuikTestApplication
using (Stream str = typeof(TestFont).Assembly.GetManifestResourceStream("QuikTestApplication.font.dat"))
{
- Texture = new byte[(int)str.Length];
- str.Read(Texture, 0, (int)str.Length);
+ TextureData = new byte[(int)str.Length];
+ str.Read(TextureData, 0, (int)str.Length);
+ }
+
+ // Evil pointer stuff. Beware!
+ Span head = MemoryMarshal.Cast(TextureData);
+ for (int i = 0; i < head.Length; i++)
+ {
+ if (head[i] == 0xff000000)
+ {
+ head[i] = 0x00000000;
+ }
}
}
@@ -85,14 +109,14 @@ namespace QuikTestApplication
return _characters.Contains((char) character);
}
- public override void GetCharacter(int character, out int texture, out QuikGlyph glyph)
+ public override void GetCharacter(int character, out IQuikTexture texture, out QuikGlyph glyph)
{
if (!_glyphs.TryGetValue(character, out glyph))
{
glyph = _glyphs['?'];
}
- texture = TextureBase;
+ texture = Texture;
}
}
}
\ No newline at end of file