diff --git a/Quik/CommandQueue/Command.cs b/Quik/CommandQueue/Command.cs
new file mode 100644
index 0000000..f85b200
--- /dev/null
+++ b/Quik/CommandQueue/Command.cs
@@ -0,0 +1,59 @@
+namespace Quik.CommandQueue
+{
+ ///
+ /// Enumeration of built-in Quik commands.
+ ///
+ public enum Command
+ {
+ #region Control Commands
+ ///
+ /// Invoke a function directly.
+ ///
+ Invoke,
+
+ ///
+ /// Begin conditional rendering segment.
+ ///
+ ConditionalBegin,
+
+ ///
+ /// End conditional rendering segment.
+ ///
+ ConditionalEnd,
+
+ PushViewport,
+ StoreViewport,
+ PopViewport,
+
+ IncrementZ,
+ AddZ,
+ StoreZ,
+ DecrementZ,
+
+ PushMatrix,
+ StoreIdentityMatrix,
+ StoreMatrix,
+ PopMatrix,
+
+ PushStyle,
+ StoreStyle,
+ PopStyle,
+ #endregion
+
+ #region Draw Commands
+ Line,
+ Bezier,
+ Rectangle,
+ Ellipse,
+ Triangle,
+ Polygon,
+ Image,
+ #endregion
+
+
+ ///
+ /// Start index for custom commands.
+ ///
+ CustomCommandBase = 1024,
+ }
+}
\ No newline at end of file
diff --git a/Quik/CommandQueue/CommandHandler.cs b/Quik/CommandQueue/CommandHandler.cs
new file mode 100644
index 0000000..01e2b67
--- /dev/null
+++ b/Quik/CommandQueue/CommandHandler.cs
@@ -0,0 +1,8 @@
+namespace Quik.CommandQueue
+{
+ ///
+ /// A delegate for a QUIK command.
+ ///
+ /// The current stack.
+ public delegate void QuikCommandHandler(object state);
+}
\ No newline at end of file
diff --git a/Quik/CommandQueue/CommandQueue.cs b/Quik/CommandQueue/CommandQueue.cs
new file mode 100644
index 0000000..862bd9c
--- /dev/null
+++ b/Quik/CommandQueue/CommandQueue.cs
@@ -0,0 +1,232 @@
+using System;
+using System.Collections.Generic;
+
+namespace Quik.CommandQueue
+{
+ public class CommandQueue : Queue
+ {
+ public void Invoke(QuikCommandHandler handler)
+ {
+ Enqueue(Command.Invoke);
+ Enqueue(new Frame(handler));
+ }
+
+ public void ConditionalBegin(bool value)
+ {
+ Enqueue(Command.ConditionalBegin);
+ Enqueue((Frame)(value ? 1 : 0));
+ }
+
+ public void ConditionalBegin(Func condition)
+ {
+ Enqueue(Command.ConditionalBegin);
+ Enqueue(new Frame(condition));
+ }
+
+ public void ConditionalEnd()
+ {
+ Enqueue(Command.ConditionalEnd);
+ }
+
+ public void PushViewport(in QuikRectangle viewport)
+ {
+ Enqueue(Command.PushViewport);
+ Enqueue(viewport);
+ }
+
+ public void StoreViewport(in QuikRectangle viewport)
+ {
+ Enqueue(Command.StoreViewport);
+ Enqueue(viewport);
+ }
+
+ public void PopViewport()
+ {
+ Enqueue(Command.PopViewport);
+ }
+
+ public void IncrementZ()
+ {
+ Enqueue(Command.IncrementZ);
+ }
+
+ public void AddZ(int value)
+ {
+ if (value == 1)
+ {
+ IncrementZ();
+ }
+ else if (value == -1)
+ {
+ DecrementZ();
+ }
+ else
+ {
+ Enqueue(Command.AddZ);
+ Enqueue((Frame)value);
+ }
+ }
+
+ public void StoreZ(int value)
+ {
+ Enqueue(Command.StoreZ);
+ Enqueue((Frame)value);
+ }
+
+ public void DecrementZ()
+ {
+ Enqueue(Command.DecrementZ);
+ }
+
+ public void Line(in QuikLine line)
+ {
+ Enqueue(Command.Line);
+ Enqueue(line);
+ }
+
+ public void Line(params QuikLine[] lines)
+ {
+ Enqueue(Command.Line);
+ Enqueue((Frame)lines.Length);
+ foreach (QuikLine line in lines)
+ Enqueue(line);
+ }
+
+ public void Bezier(in QuikBezier bezier)
+ {
+ Frame a, b;
+ Frame.Create(bezier, out a, out b);
+
+ Enqueue(Command.Bezier);
+ Enqueue(a);
+ Enqueue(b);
+ }
+
+ public void Bezier(params QuikBezier[] beziers)
+ {
+ Frame a, b;
+
+ Enqueue(Command.Bezier);
+ Enqueue((Frame)beziers.Length);
+
+ foreach (QuikBezier bezier in beziers)
+ {
+ Frame.Create(bezier, out a, out b);
+ Enqueue(a);
+ Enqueue(b);
+ }
+ }
+
+ public void Rectangle(in QuikRectangle rectangle)
+ {
+ Enqueue(Command.Rectangle);
+ Enqueue(rectangle);
+ }
+
+ public void Rectangle(QuikRectangle[] rectangles)
+ {
+ Enqueue(Command.Rectangle);
+ Enqueue((Frame)rectangles.Length);
+ foreach (QuikRectangle rectangle in rectangles)
+ Enqueue(rectangle);
+ }
+
+ public void Ellipse(in QuikEllipse ellipse)
+ {
+ Frame a, b;
+ Frame.Create(ellipse, out a, out b);
+
+ Enqueue(Command.Ellipse);
+ Enqueue(a);
+ Enqueue(b);
+ }
+
+ public void Ellipse(params QuikEllipse[] ellipses)
+ {
+ Frame a, b;
+ Enqueue(Command.Ellipse);
+ Enqueue((Frame)ellipses.Length);
+ foreach (QuikEllipse ellipse in ellipses)
+ {
+ Frame.Create(ellipse, out a, out b);
+ Enqueue(a);
+ Enqueue(b);
+ }
+ }
+
+ public void Triangle(in QuikTriangle triangle)
+ {
+ Enqueue(Command.Triangle);
+ Enqueue(triangle.A);
+ Enqueue(triangle.B);
+ Enqueue(triangle.C);
+ }
+
+ public void Triangle(params QuikTriangle[] triangles)
+ {
+ Enqueue(Command.Triangle);
+ Enqueue((Frame)triangles.Length);
+ foreach (QuikTriangle triangle in triangles)
+ {
+ Enqueue(triangle.A);
+ Enqueue(triangle.B);
+ Enqueue(triangle.C);
+ }
+ }
+
+ public void Polygon(params QuikVec2[] polygon)
+ {
+ Enqueue(Command.Polygon);
+ Enqueue((Frame)polygon.Length);
+ foreach (QuikVec2 vertex in polygon)
+ {
+ Enqueue(vertex);
+ }
+ }
+
+ public void Image(IQuikTexture texture, in QuikRectangle rectangle)
+ {
+ Enqueue(Command.Image);
+ Enqueue((Frame)(int)ImageCommandFlags.Single);
+ Enqueue(new Frame(texture));
+ Enqueue(rectangle);
+ }
+
+ public void Image(IQuikTexture texture, in QuikRectangle rectangle, in QuikRectangle uv)
+ {
+ Enqueue(Command.Image);
+ Enqueue((Frame)(int)(ImageCommandFlags.Single | ImageCommandFlags.UVs));
+ Enqueue(new Frame(texture));
+ Enqueue(rectangle);
+ Enqueue(uv);
+ }
+
+ public void Image(IQuikTexture texture, QuikRectangle[] rectangles, bool interleavedUV = false)
+ {
+ ImageCommandFlags flags = interleavedUV ? ImageCommandFlags.UVs : ImageCommandFlags.None;
+
+ Enqueue(Command.Image);
+ Enqueue(new Frame((int)flags, rectangles.Length / 2));
+ Enqueue(new Frame(texture));
+
+ foreach (QuikRectangle rectangle in rectangles)
+ {
+ Enqueue(rectangle);
+ }
+ }
+
+ public void Image(IQuikTexture texture, QuikRectangle[] rectangles, QuikRectangle[] uvs)
+ {
+ int count = Math.Min(rectangles.Length, uvs.Length);
+ Enqueue(Command.Image);
+ Enqueue(new Frame((int)ImageCommandFlags.UVs, count));
+ Enqueue(new Frame(texture));
+
+ for (int i = 0; i < count; i++)
+ {
+ Enqueue(rectangles[i]);
+ Enqueue(uvs[i]);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Quik/CommandQueue/Frame.cs b/Quik/CommandQueue/Frame.cs
new file mode 100644
index 0000000..f6fbd3b
--- /dev/null
+++ b/Quik/CommandQueue/Frame.cs
@@ -0,0 +1,212 @@
+using System.Runtime.InteropServices;
+
+namespace Quik.CommandQueue
+{
+ [StructLayout(LayoutKind.Explicit)]
+ public struct Frame
+ {
+ [FieldOffset(0)]
+ private FrameType _type;
+
+ [FieldOffset(sizeof(FrameType) + 0*sizeof(int))]
+ private int _i1;
+ [FieldOffset(sizeof(FrameType) + 1*sizeof(int))]
+ private int _i2;
+ [FieldOffset(sizeof(FrameType) + 2*sizeof(int))]
+ private int _i3;
+ [FieldOffset(sizeof(FrameType) + 3*sizeof(int))]
+ private int _i4;
+
+ [FieldOffset(sizeof(FrameType) + 0*sizeof(float))]
+ private float _f1;
+ [FieldOffset(sizeof(FrameType) + 1*sizeof(float))]
+ private float _f2;
+ [FieldOffset(sizeof(FrameType) + 2*sizeof(float))]
+ private float _f3;
+ [FieldOffset(sizeof(FrameType) + 3*sizeof(float))]
+ private float _f4;
+
+ [FieldOffset(sizeof(FrameType))]
+ private object _object;
+
+ public bool IsCommand => _type == FrameType.Command;
+ public bool IsInteger =>
+ _type == FrameType.IVec1 ||
+ _type == FrameType.IVec2 ||
+ _type == FrameType.IVec3 ||
+ _type == FrameType.IVec4;
+ public bool IsFloat =>
+ _type == FrameType.IVec1 ||
+ _type == FrameType.IVec2 ||
+ _type == FrameType.IVec3 ||
+ _type == FrameType.IVec4;
+
+ public int VectorSize
+ {
+ get
+ {
+ switch (_type)
+ {
+ case FrameType.None:
+ return 0;
+ default:
+ return 1;
+ case FrameType.Vec2: case FrameType.IVec2:
+ return 2;
+ case FrameType.Vec3: case FrameType.IVec3:
+ return 3;
+ case FrameType.Vec4: case FrameType.IVec4:
+ return 4;
+ }
+ }
+ }
+
+ public static Frame None { get; } = new Frame() {
+ _type = FrameType.None
+ };
+
+ public Frame(Command command) : this((int)command)
+ {
+ }
+
+ public Frame(object o)
+ {
+ _type = FrameType.Object;
+
+ _i1 = _i2 = _i3 = _i4 = default;
+ _f1 = _f2 = _f3 = _f4 = default;
+ _object = null;
+
+ _object = o;
+ }
+
+ public Frame(int i1)
+ {
+ _type = FrameType.IVec1;
+
+ _i1 = _i2 = _i3 = _i4 = default;
+ _f1 = _f2 = _f3 = _f4 = default;
+ _object = null;
+
+ _i1 = i1;
+ }
+
+ public Frame(int i1, int i2)
+ {
+ _type = FrameType.IVec2;
+
+ _i1 = _i2 = _i3 = _i4 = default;
+ _f1 = _f2 = _f3 = _f4 = default;
+ _object = null;
+
+ _i1 = i1;
+ _i2 = i2;
+ }
+
+ public Frame(int i1, int i2, int i3)
+ {
+ _type = FrameType.IVec3;
+
+ _i1 = _i2 = _i3 = _i4 = default;
+ _f1 = _f2 = _f3 = _f4 = default;
+ _object = null;
+
+ _i1 = i1;
+ _i2 = i2;
+ _i3 = i3;
+ }
+
+ public Frame(int i1, int i2, int i3, int i4)
+ {
+ _type = FrameType.IVec4;
+
+ _i1 = _i2 = _i3 = _i4 = default;
+ _f1 = _f2 = _f3 = _f4 = default;
+ _object = null;
+
+ _i1 = i1;
+ _i2 = i2;
+ _i3 = i3;
+ _i4 = i4;
+ }
+
+ public Frame(float f1)
+ {
+ _type = FrameType.Vec1;
+
+ _i1 = _i2 = _i3 = _i4 = default;
+ _f1 = _f2 = _f3 = _f4 = default;
+ _object = null;
+
+ _f1 = f1;
+ }
+
+ public Frame(float f1, float f2)
+ {
+ _type = FrameType.Vec2;
+
+ _i1 = _i2 = _i3 = _i4 = default;
+ _f1 = _f2 = _f3 = _f4 = default;
+ _object = null;
+
+ _f1 = f1;
+ _f2 = f2;
+ }
+
+ public Frame(float f1, float f2, float f3)
+ {
+ _type = FrameType.Vec3;
+
+ _i1 = _i2 = _i3 = _i4 = default;
+ _f1 = _f2 = _f3 = _f4 = default;
+ _object = null;
+
+ _f1 = f1;
+ _f2 = f2;
+ _f3 = f3;
+ }
+
+ public Frame(float f1, float f2, float f3, float f4)
+ {
+ _type = FrameType.Vec4;
+
+ _i1 = _i2 = _i3 = _i4 = default;
+ _f1 = _f2 = _f3 = _f4 = default;
+ _object = null;
+
+ _f1 = f1;
+ _f2 = f2;
+ _f3 = f3;
+ _f4 = f4;
+ }
+
+ public T As() where T : class
+ {
+ return (T)_object;
+ }
+
+ public static implicit operator int(in Frame frame) => frame._i1;
+ public static implicit operator float(in Frame frame) => frame._f1;
+ public static implicit operator Command(in Frame frame) => (Command)frame._i1;
+
+ public static explicit operator Frame(int i) => new Frame(i);
+ public static explicit operator Frame(float f) => new Frame(f);
+ public static implicit operator Frame(Command cmd) => new Frame(cmd);
+ public static implicit operator Frame(in QuikVec2 vector) => new Frame(vector.X, vector.Y);
+ public static implicit operator Frame(in QuikColor color) => new Frame(color.R, color.G, color.B, color.A);
+ public static implicit operator Frame(in QuikRectangle rect) => new Frame(rect.Min.X, rect.Min.Y, rect.Max.X, rect.Max.Y);
+ public static implicit operator Frame(in QuikLine line) => new Frame(line.Start.X, line.Start.Y, line.End.X, line.Start.Y);
+
+ public static void Create(in QuikBezier bezier, out Frame a, out Frame b)
+ {
+ a = new Frame(bezier.Start.X, bezier.Start.Y, bezier.End.X, bezier.End.Y);
+ b = new Frame(bezier.ControlA.X, bezier.ControlA.Y, bezier.ControlB.X, bezier.ControlB.Y);
+ }
+
+ public static void Create(in QuikEllipse ellipse, out Frame a, out Frame b)
+ {
+ a = new Frame(ellipse.Center.X, ellipse.Center.Y);
+ b = new Frame(ellipse.AxisA.X, ellipse.AxisA.Y, ellipse.AxisB.X, ellipse.AxisB.Y);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Quik/CommandQueue/FrameType.cs b/Quik/CommandQueue/FrameType.cs
new file mode 100644
index 0000000..6bc6344
--- /dev/null
+++ b/Quik/CommandQueue/FrameType.cs
@@ -0,0 +1,17 @@
+namespace Quik.CommandQueue
+{
+ public enum FrameType
+ {
+ None,
+ Command,
+ IVec1,
+ IVec2,
+ IVec3,
+ IVec4,
+ Vec1,
+ Vec2,
+ Vec3,
+ Vec4,
+ Object,
+ }
+}
\ No newline at end of file
diff --git a/Quik/CommandQueue/ImageCommandFlags.cs b/Quik/CommandQueue/ImageCommandFlags.cs
new file mode 100644
index 0000000..1374183
--- /dev/null
+++ b/Quik/CommandQueue/ImageCommandFlags.cs
@@ -0,0 +1,10 @@
+namespace Quik.CommandQueue
+{
+ public enum ImageCommandFlags
+ {
+ None = 0,
+
+ Single = 1 << 0,
+ UVs = 1 << 1
+ }
+}
\ No newline at end of file
diff --git a/Quik/IQuikTexture.cs b/Quik/IQuikTexture.cs
index 172976a..98bb9ec 100644
--- a/Quik/IQuikTexture.cs
+++ b/Quik/IQuikTexture.cs
@@ -29,7 +29,8 @@ namespace Quik
/// Color format of the data.
/// Size of the texture data.
/// Mip level.
- void Image(IntPtr data, QuikImageFormat format, QuikVec2 size, int level);
+ /// Pixel alignment. Expected to be 1, 2, 4, or 8.
+ void Image(IntPtr data, QuikImageFormat format, QuikVec2 size, int level, int alignment = 4);
///
/// Upload texture data.
@@ -38,7 +39,8 @@ namespace Quik
/// Color format for the data.
/// Location of the data in the texture.
/// Mip level.
- void SubImage(IntPtr data, QuikImageFormat format, QuikRectangle location, int level);
+ /// Pixel alignment. Expected to be 1, 2, 4, or 8.
+ void SubImage(IntPtr data, QuikImageFormat format, QuikRectangle location, int level, int alignment = 4);
///
/// Generate the mip maps for the texture.
diff --git a/Quik/QuikGeometry.cs b/Quik/QuikGeometry.cs
index 5d140fe..f781fec 100644
--- a/Quik/QuikGeometry.cs
+++ b/Quik/QuikGeometry.cs
@@ -308,6 +308,19 @@ namespace Quik
Max = new QuikVec2() {X = r, Y = t};
Min = new QuikVec2() {X = l, Y = b};
}
+
+ public bool Contains(QuikVec2 point)
+ {
+ return
+ point.X > Left && point.X < Right &&
+ point.Y > Bottom && point.Y < Top;
+ }
+
+ internal void Translate(in QuikVec2 offset)
+ {
+ Min += offset;
+ Max += offset;
+ }
}
///
diff --git a/Quik/QuikImageFormat.cs b/Quik/QuikImageFormat.cs
index 986b232..63efbdd 100644
--- a/Quik/QuikImageFormat.cs
+++ b/Quik/QuikImageFormat.cs
@@ -7,6 +7,8 @@ namespace Quik
RgbaU8,
RedF,
RgbF,
- RgbaF
+ RgbaF,
+ AlphaU8,
+ AlphaF
}
}
\ No newline at end of file