13 Commits

Author SHA1 Message Date
themixedupstuff 362165f911 Push a fix for the upstream issue in ReFuel. 2026-08-24 22:01:20 +03:00
themixedupstuff 008882cb81 Move framework projects to their own folders. 2026-08-24 21:49:12 +03:00
themixedupstuff a6dbba19b4 Remove archaic code from the working tree. 2026-08-24 21:46:18 +03:00
themixedupstuff 6665a023c8 Refactor ImmediateMode rendering logic to use DrawImmediate. 2026-08-23 22:04:46 +03:00
themixedupstuff 3b20c53088 Refactor rendering interfaces and implementations
- Refactored DirectRendering class to support new IDirectRendering interface and manage textures directly.
- Enhanced ImmediateMode to utilize ImmediateVertex struct for immediate rendering calls.
- Updated Image class to use IDirectRendering for texture creation.
- Modified test application to demonstrate immediate mode rendering with new structures.
- Removed obsolete code related to previous rendering abstractions.
2026-08-23 21:44:05 +03:00
themixedupstuff bda8e173c9 Use new syntax to remove compile time warning from Application.Current. 2026-08-23 20:56:19 +03:00
themixedupstuff 1b5e13eb10 Add new SVG resources for dashboard components. 2026-08-23 17:42:43 +03:00
themixedupstuff 97f8a29f19 Refactor DrawCall to use VertexSpecification and update related rendering logic 2026-08-20 20:54:01 +03:00
themixedupstuff abdb78eae7 Move to blurgtext stable. 2026-08-11 10:12:11 +03:00
themixedupstuff 99a853f35d Use th new API in the demo. 2026-08-11 10:10:01 +03:00
themixedupstuff 3d0f15af45 Don't set the depth state twice. 2026-08-11 10:07:36 +03:00
themixedupstuff 01d7f27dc7 Fix the use of glCreateXXX (4.2) instead of glGenXXX (1.0). 2026-08-11 10:07:11 +03:00
themixedupstuff 7bf7b6c53c Add a configure blurg method to the extension factory interface. 2026-08-11 10:06:35 +03:00
81 changed files with 2910 additions and 4059 deletions
+38 -12
View File
@@ -1,3 +1,4 @@
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Drawing; using System.Drawing;
using System.Numerics; using System.Numerics;
@@ -176,11 +177,18 @@ namespace Dashboard.Drawing
public int Divisor { get; init; } = 1; public int Divisor { get; init; } = 1;
} }
public record VertexSpecification(
ImmutableList<VertexAttribute> Attributes,
ImmutableList<IBuffer?> VertexBuffers
)
{
public IBuffer? ElementBuffer { get; init; } = null;
}
public record struct UniformDescriptor(int Location, UniformType Type, long Offset, long Size); public record struct UniformDescriptor(int Location, UniformType Type, long Offset, long Size);
public struct DrawCall(MeshPrimitive primitive, IBuffer vertexBuffer, int first, int count) public struct PipelineState()
{ {
public IShader? ShaderPipeline { get; init; }
public Box2d ViewportRegion { get; init; } = new Box2d(Vector2.Zero, Vector2.PositiveInfinity); public Box2d ViewportRegion { get; init; } = new Box2d(Vector2.Zero, Vector2.PositiveInfinity);
public Box2d ScissorRegion { get; init; } = new Box2d(Vector2.PositiveInfinity, Vector2.PositiveInfinity); public Box2d ScissorRegion { get; init; } = new Box2d(Vector2.PositiveInfinity, Vector2.PositiveInfinity);
public WindingOrder FrontFace { get; init; } = WindingOrder.Counterclockwise; public WindingOrder FrontFace { get; init; } = WindingOrder.Counterclockwise;
@@ -188,28 +196,46 @@ namespace Dashboard.Drawing
public BlendMode BlendMode { get; init; } = BlendMode.Normal; public BlendMode BlendMode { get; init; } = BlendMode.Normal;
public DepthMode DepthMode { get; init; } = DepthMode.Enabled; public DepthMode DepthMode { get; init; } = DepthMode.Enabled;
public StencilMode StencilMode { get; init; } = StencilMode.Off; public StencilMode StencilMode { get; init; } = StencilMode.Off;
public bool EnableRestart { get; init; } = false;
public long RestartIndex { get; init; } = -1;
public bool RedMask { get; init; } = true; public bool RedMask { get; init; } = true;
public bool GreenMask { get; init; } = true; public bool GreenMask { get; init; } = true;
public bool BlueMask { get; init; } = true; public bool BlueMask { get; init; } = true;
public bool AlphaMask { get; init; } = true; public bool AlphaMask { get; init; } = true;
public float PointSize { get; init; }= 1.0f; public float PointSize { get; init; }= 1.0f;
public float LineWidth { get; init; }= 1.0f; public float LineWidth { get; init; }= 1.0f;
}
public struct DrawCall(MeshPrimitive primitive, VertexSpecification vertexSpec, int first, int count)
{
public IShader? ShaderPipeline { get; init; }
public PipelineState PipelineState { get; init; } = new PipelineState();
public VertexSpecification VertexSpecification { get; init; } = vertexSpec;
public MeshPrimitive Primitive { get; init; } = primitive; public MeshPrimitive Primitive { get; init; } = primitive;
public int First { get; init; } = first;
public int Count { get; init; } = count;
public int BaseVertex { get; init; } = 0;
public long Offset { get; init; } = 0;
public bool EnableRestart { get; init; } = false;
public long RestartIndex { get; init; } = -1;
public Box2d ViewportRegion => PipelineState.ViewportRegion;
public Box2d ScissorRegion => PipelineState.ScissorRegion;
public WindingOrder FrontFace => PipelineState.FrontFace;
public FaceCulling CullMode => PipelineState.CullMode;
public BlendMode BlendMode => PipelineState.BlendMode;
public DepthMode DepthMode => PipelineState.DepthMode;
public StencilMode StencilMode => PipelineState.StencilMode;
public bool RedMask => PipelineState.RedMask;
public bool GreenMask => PipelineState.GreenMask;
public bool BlueMask => PipelineState.BlueMask;
public bool AlphaMask => PipelineState.AlphaMask;
public float PointSize => PipelineState.PointSize;
public float LineWidth => PipelineState.LineWidth;
public List<ITexture?> Textures { get; init; } = []; public List<ITexture?> Textures { get; init; } = [];
public List<VertexAttribute> Attributes { get; init; } = [];
public List<UniformDescriptor> Uniforms { get; init; } = []; public List<UniformDescriptor> Uniforms { get; init; } = [];
public IndexType IndexType { get; init; } = IndexType.UnsignedShort; public IndexType IndexType { get; init; } = IndexType.UnsignedShort;
public int First { get; init; } = first;
public int Count { get; init; } = count;
public long Offset { get; init; } = 0;
public int BaseVertex { get; init; } = 0;
public IBuffer VertexBuffer { get; init; } = vertexBuffer;
public IBuffer? ElementBuffer { get; init; }
public ReadOnlyMemory<byte> UniformData { get; init; } = ReadOnlyMemory<byte>.Empty; public ReadOnlyMemory<byte> UniformData { get; init; } = ReadOnlyMemory<byte>.Empty;
public IBuffer? UniformBuffer { get; init; } public IBuffer? UniformBuffer { get; init; }
} }
+43
View File
@@ -0,0 +1,43 @@
namespace Dashboard.Drawing
{
public enum BufferAccessPattern
{
/// <summary>
/// No hint given. Not recommended.
/// </summary>
DontCare,
/// <summary>
/// For buffers which have a long lifetime, but the contents are initialized once.
/// </summary>
Static,
/// <summary>
/// For buffers which have a long lifetime, but the contents can be updated every frame.
/// </summary>
Stream,
/// <summary>
/// For buffers which have a short lifetime, used for streaming resources to the graphics hardware.
/// </summary>
Upload,
/// <summary>
/// For buffers which have a short lifetime, used for streaming resources from the graphics hardware.
/// </summary>
Download,
}
public interface IBuffer : IDisposable
{
public BufferAccessPattern AccessPattern { get; }
public long Size { get; }
void Reallocate(long newSize);
Span<T> Map<T>(long offset, int count = -1) where T : unmanaged;
void Unmap();
void Read<T>(long offset, Span<T> span) where T : unmanaged;
void Write<T>(long offset, ReadOnlySpan<T> span) where T : unmanaged;
}
}
+36 -53
View File
@@ -2,66 +2,49 @@ using Dashboard.Pal;
namespace Dashboard.Drawing namespace Dashboard.Drawing
{ {
public interface IShader : IDisposable
{
}
/// <summary>
/// Base interface for creating shader pipelines.
/// </summary>
public interface IShaderCreateInfo
{
}
public enum BufferAccessPattern
{
/// <summary>
/// No hint given. Not recommended.
/// </summary>
DontCare,
/// <summary>
/// For buffers which have a long lifetime, but the contents are initialized once.
/// </summary>
Static,
/// <summary>
/// For buffers which have a long lifetime, but the contents can be updated every frame.
/// </summary>
Stream,
/// <summary>
/// For buffers which have a short lifetime, used for streaming resources to the graphics hardware.
/// </summary>
Upload,
/// <summary>
/// For buffers which have a short lifetime, used for streaming resources from the graphics hardware.
/// </summary>
Download,
}
public interface IBuffer : IDisposable
{
public BufferAccessPattern AccessPattern { get; }
public long Size { get; }
void Reallocate(long newSize);
Span<T> Map<T>(long offset, int count = -1) where T : unmanaged;
void Unmap();
void Read<T>(long offset, Span<T> span) where T : unmanaged;
void Write<T>(long offset, ReadOnlySpan<T> span) where T : unmanaged;
}
/// <summary> /// <summary>
/// This extension provides direct access to the backend rendering pipeline without writing with a higher level /// This extension provides direct access to the backend rendering pipeline without writing with a higher level
/// abstraction than the rendering backend. /// abstraction than the rendering backend.
/// </summary> /// </summary>
public interface IDirectRendering : IDeviceContextExtension public interface IDirectRendering : IDeviceContextExtension
{ {
public IShader CreatePipeline<T>(T createInfo) where T : IShaderCreateInfo; /// <summary>
public IBuffer CreateBuffer(BufferAccessPattern pattern, long size); /// Create a shader program.
/// </summary>
/// <typeparam name="T">Type of the shader create info object.</typeparam>
/// <param name="createInfo">An object which describes how the shader is created.</param>
/// <returns>A shader program object.</returns>
/// <seealso cref="SpirvShaderCreateInfo"/>
/// <seealso cref="GlslShaderCreateInfo"/>
/// <seealso cref="GlslComputeShaderCreateInfo"/>
IShader CreateShader<T>(T createInfo) where T : IShaderCreateInfo;
/// <summary>
/// Create a texture object.
/// </summary>
/// <param name="type">Type of texture to create.</param>
/// <returns>An empty texture object.</returns>
ITexture CreateTexture(TextureType type);
/// <summary>
/// Create a buffer object.
/// </summary>
/// <param name="pattern">Buffer access pattern hint.</param>
/// <param name="size">Initial capacity of the buffer in bytes.</param>
/// <returns>An empty buffer object.</returns>
IBuffer CreateBuffer(BufferAccessPattern pattern, long size);
/// <summary>
/// Enqueue a draw call.
/// </summary>
/// <param name="drawCall">The draw call to enqueue.</param>
void Draw(DrawCall drawCall); void Draw(DrawCall drawCall);
} }
[Obsolete("Use IDirectRendering instead.")]
public interface ITextureExtension : IDeviceContextExtension
{
/// <inheritdoc cref="IDirectRendering.CreateTexture(TextureType)"/>
ITexture CreateTexture(TextureType type);
}
} }
@@ -1,5 +1,7 @@
using System.Data;
using System.Drawing; using System.Drawing;
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices;
using Dashboard.Layout; using Dashboard.Layout;
using Dashboard.Pal; using Dashboard.Pal;
@@ -7,8 +9,57 @@ namespace Dashboard.Drawing
{ {
public record struct RectangleDrawInfo(Vector2 Position, ComputedBox Box, Brush Fill, Brush? Border = null); public record struct RectangleDrawInfo(Vector2 Position, ComputedBox Box, Brush Fill, Brush? Border = null);
[StructLayout(LayoutKind.Explicit, Size = Size)]
public struct ImmediateVertex()
{
[field: FieldOffset(PosOffset)]
public Vector3 Position { get; init; }= Vector3.Zero;
[field: FieldOffset(TexCoordsOffset)]
public Vector2 TexCoords { get; init; } = Vector2.Zero;
[field: FieldOffset(ColorOffset)]
public Vector4 Color { get; init; } = Vector4.One;
public ImmediateVertex(Vector3 position, Vector2 texCoords, Vector4 color) : this()
{
Position = position;
TexCoords = texCoords;
Color = color;
}
public ImmediateVertex(Vector3 position, Vector2 texCoords, Color color)
: this(
position,
texCoords,
new Vector4(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f))
{
}
public const int Size = 16 * sizeof(float);
public const int PosOffset = 0 * sizeof(float);
public const int TexCoordsOffset = 4 * sizeof(float);
public const int ColorOffset = 8 * sizeof(float);
}
public struct ImmediateDrawCall(MeshPrimitive primitive, ReadOnlyMemory<ImmediateVertex> vertices)
{
public MeshPrimitive Primitive { get; init; } = primitive;
public ReadOnlyMemory<ImmediateVertex> Vertices { get; init; } = vertices;
public PipelineState PipelineState { get; init; } = new PipelineState();
public Matrix4x4 Transforms { get; init; } = Matrix4x4.Identity;
public ITexture? Texture { get; init; } = null;
}
public interface IImmediateMode : IDeviceContextExtension public interface IImmediateMode : IDeviceContextExtension
{ {
/// <summary>
/// Enqueue a draw with the immediate mode shader pipeline.
/// </summary>
/// <param name="call">Information about this draw call.</param>
void DrawImmediate(ImmediateDrawCall call);
// TODO: move these to their own extension?
void Line(Vector2 a, Vector2 b, float width, float depth, Vector4 color); void Line(Vector2 a, Vector2 b, float width, float depth, Vector4 color);
void Rectangle(Box2d rectangle, float depth, Vector4 color); void Rectangle(Box2d rectangle, float depth, Vector4 color);
void Rectangle(in RectangleDrawInfo rectangle); void Rectangle(in RectangleDrawInfo rectangle);
+21
View File
@@ -0,0 +1,21 @@
using System.Collections.Immutable;
namespace Dashboard.Drawing
{
/// <summary>
/// Base interface for creating shader pipelines.
/// </summary>
public interface IShaderCreateInfo
{
}
public readonly record struct ShaderMappingProperty(string Name, int Index);
public interface IShader : IDisposable
{
public ImmutableList<ShaderMappingProperty> Attributes { get; }
public ImmutableList<ShaderMappingProperty> Uniforms { get; }
public ImmutableList<ShaderMappingProperty> Blocks { get; }
public ImmutableList<ShaderMappingProperty> Textures { get; }
}
}
@@ -1,13 +1,7 @@
using System.Drawing; using System.Drawing;
using Dashboard.Pal;
namespace Dashboard.Drawing namespace Dashboard.Drawing
{ {
public interface ITextureExtension : IDeviceContextExtension
{
ITexture CreateTexture(TextureType type);
}
public enum TextureType public enum TextureType
{ {
Texture1D, Texture1D,
+1 -1
View File
@@ -30,7 +30,7 @@ namespace Dashboard.Drawing
if (Textures.TryGetValue(dc, out ITexture? texture)) if (Textures.TryGetValue(dc, out ITexture? texture))
return texture; return texture;
ITextureExtension ext = dc.ExtensionRequire<ITextureExtension>(); IDirectRendering ext = dc.ExtensionRequire<IDirectRendering>();
texture = ext.CreateTexture(Type); texture = ext.CreateTexture(Type);
texture.SetStorage(Format, Width, Height, Depth, Levels); texture.SetStorage(Format, Width, Height, Depth, Levels);
for (int i = 0; i < Levels; i++) for (int i = 0; i < Levels; i++)
+3 -3
View File
@@ -192,11 +192,11 @@ namespace Dashboard.Pal
public void Dispose() => InvokeDispose(true); public void Dispose() => InvokeDispose(true);
[ThreadStatic] private static Application _current; [field: ThreadStatic]
public static Application Current public static Application Current
{ {
get => _current ?? throw new InvalidOperationException("There is currently no current application."); get => field ?? throw new InvalidOperationException("There is currently no current application.");
set => _current = value; set;
} }
} }
} }
-48
View File
@@ -1,48 +0,0 @@
using System.Numerics;
using System.Runtime.InteropServices;
namespace Dashboard.Drawing.OpenGL
{
public enum SimpleDrawCommand : int
{
Point = 1,
Line = 2,
Rect = 3,
/// <summary>
/// Make sure your custom commands have values higher than this if you plan on using the default command
/// buffer.
/// </summary>
CustomCommandStart = 4096
}
[StructLayout(LayoutKind.Explicit, Size = 64)]
public struct CommandInfo
{
[FieldOffset(0)]
public SimpleDrawCommand Type;
[FieldOffset(4)]
public int Flags;
[FieldOffset(8)]
public float Arg0;
[FieldOffset(12)]
public float Arg1;
[FieldOffset(16)]
public int FgGradientIndex;
[FieldOffset(20)]
public int FgGradientCount;
[FieldOffset(24)]
public int BgGradientIndex;
[FieldOffset(28)]
public int BgGradientCount;
[FieldOffset(32)]
public Vector4 FgColor;
[FieldOffset(48)]
public Vector4 BgColor;
}
}
-184
View File
@@ -1,184 +0,0 @@
using System.Drawing;
using Dashboard.Drawing.OpenGL.Executors;
using Dashboard.OpenGL;
using OpenTK.Mathematics;
namespace Dashboard.Drawing.OpenGL
{
public interface ICommandExecutor
{
IEnumerable<string> Extensions { get; }
IContextExecutor Executor { get; }
void SetContextExecutor(IContextExecutor executor);
void BeginFrame();
void BeginDraw();
void EndDraw();
void EndFrame();
void ProcessCommand(ICommandFrame frame);
}
public interface IContextExecutor : IInitializer, IGLDisposable
{
GLEngine Engine { get; }
IGLContext Context { get; }
ContextResourcePool ResourcePool { get; }
TransformStack TransformStack { get; }
}
public class ContextExecutor : IContextExecutor
{
public GLEngine Engine { get; }
public IGLContext Context { get; }
public ContextResourcePool ResourcePool { get; }
public TransformStack TransformStack { get; } = new TransformStack();
protected bool IsDisposed { get; private set; } = false;
public bool IsInitialized { get; private set; } = false;
private readonly List<ICommandExecutor> _executorsList = new List<ICommandExecutor>();
private readonly Dictionary<string, ICommandExecutor> _executorsMap = new Dictionary<string, ICommandExecutor>();
public ContextExecutor(GLEngine engine, IGLContext context)
{
Engine = engine;
Context = context;
ResourcePool = Engine.ResourcePoolManager.Get(context);
ResourcePool.IncrementReference();
AddExecutor(new BaseCommandExecutor());
AddExecutor(new TextCommandExecutor());
}
~ContextExecutor()
{
DisposeInvoker(true, false);
}
public void AddExecutor(ICommandExecutor executor, bool overwrite = false)
{
if (IsInitialized)
throw new Exception("This context executor is already initialized. Cannot add new command executors.");
IInitializer? initializer = executor as IInitializer;
if (initializer?.IsInitialized == true)
throw new InvalidOperationException("This command executor has already been initialized, cannot add here.");
if (!overwrite)
{
foreach (string extension in executor.Extensions)
{
if (_executorsMap.ContainsKey(extension))
throw new InvalidOperationException("An executor already handles this extension.");
}
}
foreach (string extension in executor.Extensions)
{
_executorsMap[extension] = executor;
}
_executorsList.Add(executor);
executor.SetContextExecutor(this);
}
public void Initialize()
{
if (IsInitialized)
return;
IsInitialized = true;
foreach (ICommandExecutor executor in _executorsList)
{
if (executor is IInitializer initializer)
initializer.Initialize();
}
}
public virtual void BeginFrame()
{
foreach (ICommandExecutor executor in _executorsList)
executor.BeginFrame();
}
protected virtual void BeginDraw()
{
foreach (ICommandExecutor executor in _executorsList)
executor.BeginDraw();
}
protected virtual void EndDraw()
{
foreach (ICommandExecutor executor in _executorsList)
executor.EndDraw();
}
public virtual void EndFrame()
{
ResourcePool.Collector.Dispose();
TransformStack.Clear();
foreach (ICommandExecutor executor in _executorsList)
executor.EndFrame();
}
public void Draw(DrawQueue drawqueue) => Draw(drawqueue, new RectangleF(new PointF(0f,0f), Context.FramebufferSize));
public virtual void Draw(DrawQueue drawQueue, RectangleF bounds, float scale = 1.0f)
{
BeginDraw();
if (scale != 1.0f)
TransformStack.Push(Matrix4.CreateScale(scale, scale, 1));
foreach (ICommandFrame frame in drawQueue)
{
if (_executorsMap.TryGetValue(frame.Command.Extension.Name, out ICommandExecutor? executor))
executor.ProcessCommand(frame);
}
EndDraw();
}
private void DisposeInvoker(bool safeExit, bool disposing)
{
if (!IsDisposed)
return;
IsDisposed = true;
if (disposing)
GC.SuppressFinalize(this);
Dispose(safeExit, disposing);
}
protected virtual void Dispose(bool safeExit, bool disposing)
{
if (disposing)
{
foreach (ICommandExecutor executor in _executorsList)
{
if (executor is IGLDisposable glDisposable)
glDisposable.Dispose(safeExit);
else if (executor is IDisposable disposable)
disposable.Dispose();
}
if (ResourcePool.DecrementReference())
Dispose();
}
}
public void Dispose() => DisposeInvoker(true, true);
public void Dispose(bool safeExit) => DisposeInvoker(safeExit, true);
}
}
@@ -1,133 +0,0 @@
using Dashboard.OpenGL;
namespace Dashboard.Drawing.OpenGL
{
public class ContextResourcePoolManager
{
private readonly Dictionary<IGLContext, ContextResourcePool> _unique = new Dictionary<IGLContext, ContextResourcePool>();
private readonly Dictionary<int, ContextResourcePool> _shared = new Dictionary<int, ContextResourcePool>();
public ContextResourcePool Get(IGLContext context)
{
if (context.ContextGroup == -1)
{
if (!_unique.TryGetValue(context, out ContextResourcePool? pool))
{
pool = new ContextResourcePool(this, context);
_unique.Add(context, pool);
}
return pool;
}
else
{
if (!_shared.TryGetValue(context.ContextGroup, out ContextResourcePool? pool))
{
pool = new ContextResourcePool(this, context.ContextGroup);
_shared.Add(context.ContextGroup, pool);
}
return pool;
}
}
internal void Disposed(ContextResourcePool pool)
{
// TODO:
}
}
public class ContextResourcePool : IGLDisposable, IArc
{
private int _references = 0;
private bool _isDisposed = false;
private readonly Dictionary<int, IResourceManager> _managers = new Dictionary<int, IResourceManager>();
public ContextResourcePoolManager Manager { get; }
public IGLContext? Context { get; private set; } = null;
public int ContextGroup { get; private set; } = -1;
public int References => _references;
public ContextCollector Collector { get; } = new ContextCollector();
internal ContextResourcePool(ContextResourcePoolManager manager, int contextGroup)
{
Manager = manager;
ContextGroup = contextGroup;
}
internal ContextResourcePool(ContextResourcePoolManager manager, IGLContext context)
{
Manager = manager;
Context = context;
}
public T GetResourceManager<T>(bool init = true) where T : IResourceManager, new()
{
int index = ManagerAtom<T>.Atom;
if (!_managers.TryGetValue(index, out IResourceManager? resourceClass))
{
_managers[index] = resourceClass = new T();
}
if (init && resourceClass is IInitializer initializer)
{
initializer.Initialize();
}
return (T)resourceClass;
}
~ContextResourcePool()
{
Dispose(true, false);
}
public void Dispose() => Dispose(true, false);
public void Dispose(bool safeExit) => Dispose(safeExit, true);
private void Dispose(bool safeExit, bool disposing)
{
if (_isDisposed)
return;
_isDisposed = true;
Manager.Disposed(this);
if (disposing)
{
foreach ((int _, IResourceManager manager) in _managers)
{
if (manager is IGLDisposable glDisposable)
glDisposable.Dispose(safeExit);
else if (manager is IDisposable disposable)
disposable.Dispose();
}
GC.SuppressFinalize(this);
}
}
public void IncrementReference()
{
Interlocked.Increment(ref _references);
}
public bool DecrementReference()
{
return Interlocked.Decrement(ref _references) == 0;
}
private class ManagerAtom
{
private static int _counter = -1;
protected static int Acquire() => Interlocked.Increment(ref _counter);
}
private class ManagerAtom<T> : ManagerAtom where T : IResourceManager
{
public static int Atom { get; } = Acquire();
}
}
}
@@ -1,30 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BlurgText" Version="0.1.0-nightly-19" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Dashboard.Drawing\Dashboard.Drawing.csproj" />
<ProjectReference Include="..\Dashboard.OpenGL\Dashboard.OpenGL.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Executors\simple.frag" />
<EmbeddedResource Include="Executors\simple.vert" />
<EmbeddedResource Include="Executors\text.vert" />
<EmbeddedResource Include="Executors\text.frag" />
</ItemGroup>
<ItemGroup>
<Folder Include="Text\" />
</ItemGroup>
</Project>
@@ -1,468 +0,0 @@
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Dashboard.OpenGL;
using OpenTK.Graphics.OpenGL;
using OpenTK.Mathematics;
using Vector2 = System.Numerics.Vector2;
using Vector3 = System.Numerics.Vector3;
namespace Dashboard.Drawing.OpenGL
{
public class DrawCallRecorder : IGLDisposable, IInitializer
{
private int _vao = 0;
private int _vbo = 0;
private readonly List<DrawVertex> _vertices = new List<DrawVertex>();
private readonly List<DrawCall> _calls = new List<DrawCall>();
private int _start = 0;
private int _count = 0;
private int _primitives = 0;
private Vector3 _charCoords;
private int _cmdIndex;
private int _texture0, _texture1, _texture2, _texture3;
private TextureTarget _target0, _target1, _target2, _target3;
private Matrix4 _transforms = Matrix4.Identity;
public int CommandModulus = 64;
public int CommandBuffer = 0;
public int CommandSize = 64;
private int CommandByteSize => CommandModulus * CommandSize;
public int TransformsLocation { get; set; }
public void Transforms(in Matrix4 transforms)
{
_transforms = transforms;
}
public void Begin(PrimitiveType type)
{
if (_primitives != 0)
throw new InvalidOperationException("Attempt to begin new draw call before finishing previous one.");
_primitives = (int)type;
_start = _vertices.Count;
_count = 0;
}
public void TexCoords2(Vector2 texCoords)
{
_charCoords = new Vector3(texCoords, 0);
}
public void CharCoords(Vector3 charCoords)
{
_charCoords = charCoords;
}
public void CommandIndex(int index)
{
_cmdIndex = index;
}
public void Vertex3(Vector3 vertex)
{
_vertices.Add(new DrawVertex()
{
Position = vertex,
CharCoords = _charCoords,
CmdIndex = _cmdIndex % CommandModulus,
});
_count++;
}
public void End()
{
if (_primitives == 0)
throw new InvalidOperationException("Attempt to end draw call before starting one.");
_calls.Add(
new DrawCall()
{
Type = (PrimitiveType)_primitives,
Start = _start,
Count = _count,
CmdIndex = _cmdIndex,
Target0 = _target0,
Target1 = _target1,
Target2 = _target2,
Target3 = _target3,
Texture0 = _texture0,
Texture1 = _texture1,
Texture2 = _texture2,
Texture3 = _texture3,
Transforms = _transforms,
});
_primitives = 0;
}
public void BindTexture(TextureTarget target, int texture) => BindTexture(target, 0, texture);
public void BindTexture(TextureTarget target, int unit, int texture)
{
switch (unit)
{
case 0:
_texture0 = 0;
_target0 = target;
break;
case 1:
_texture1 = 0;
_target1 = target;
break;
case 2:
_texture2 = 0;
_target2 = target;
break;
case 3:
_texture3 = 0;
_target3 = target;
break;
default:
throw new ArgumentOutOfRangeException(nameof(unit), "I did not write support for more than 4 textures.");
}
}
public void DrawArrays(PrimitiveType type, int first, int count)
{
throw new NotImplementedException();
}
public void Execute()
{
GL.BindVertexArray(_vao);
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo);
ReadOnlySpan<DrawVertex> vertices = CollectionsMarshal.AsSpan(_vertices);
GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Count * Unsafe.SizeOf<DrawVertex>(), vertices, BufferUsage.DynamicDraw);
foreach (DrawCall call in _calls)
{
GL.BindBufferRange(BufferTarget.UniformBuffer, 0, CommandBuffer, call.CmdIndex / CommandModulus * CommandByteSize, CommandByteSize);
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(call.Target0, call.Texture0);
GL.ActiveTexture(TextureUnit.Texture1);
GL.BindTexture(call.Target1, call.Texture1);
GL.ActiveTexture(TextureUnit.Texture2);
GL.BindTexture(call.Target2, call.Texture2);
GL.ActiveTexture(TextureUnit.Texture3);
GL.BindTexture(call.Target3, call.Texture3);
Matrix4 transforms = call.Transforms;
GL.UniformMatrix4f(TransformsLocation, 1, true, ref transforms);
GL.DrawArrays(call.Type, call.Start, call.Count);
}
}
public void Clear()
{
_vertices.Clear();
_calls.Clear();
}
public void Dispose()
{
throw new NotImplementedException();
}
public void Dispose(bool safeExit)
{
throw new NotImplementedException();
}
public bool IsInitialized { get; private set; }
public void Initialize()
{
if (IsInitialized)
return;
IsInitialized = true;
_vao = GL.CreateVertexArray();
_vbo = GL.CreateBuffer();
GL.BindVertexArray(_vao);
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo);
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 32, 0);
GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, 32, 16);
GL.VertexAttribIPointer(2, 1, VertexAttribIType.Int, 32, 28);
GL.EnableVertexAttribArray(0);
GL.EnableVertexAttribArray(1);
GL.EnableVertexAttribArray(2);
}
private struct DrawCall
{
public PrimitiveType Type;
public int Start;
public int Count;
public int CmdIndex;
public int Texture0;
public int Texture1;
public int Texture2;
public int Texture3;
public TextureTarget Target0;
public TextureTarget Target1;
public TextureTarget Target2;
public TextureTarget Target3;
public Matrix4 Transforms;
}
[StructLayout(LayoutKind.Explicit, Size = 32)]
private struct DrawVertex
{
[FieldOffset(0)]
public Vector3 Position;
[FieldOffset(16)]
public Vector3 CharCoords;
[FieldOffset(28)]
public int CmdIndex;
}
}
/// <summary>
/// A customizable immediate mode draw call queue, for the modern OpenGL user.
/// </summary>
/// <typeparam name="TCall">The call info type.</typeparam>
/// <typeparam name="TVertex">The vertex structure.</typeparam>
public abstract class DrawCallRecorder<TCall, TVertex> : IGLDisposable, IInitializer
where TVertex : unmanaged
{
/// <summary>
/// The vertex array for this queue.
/// </summary>
public int Vao { get; private set; }
/// <summary>
/// The vertex buffer for this queue.
/// </summary>
public int Vbo { get; private set; }
/// <summary>
/// Number of calls recorded in this queue.
/// </summary>
public int CallCount => Calls.Count;
/// <summary>
/// The number of total vertices recorded.
/// </summary>
public int TotalVertices => Vertices.Count;
/// <summary>
/// The latest draw call info.
/// </summary>
public ref TCall CurrentCall => ref _currentCall;
/// <summary>
/// The latest vertex emitted.
/// </summary>
public ref TVertex CurrentVertex => ref _currentVertex;
/// <summary>
/// True if currently recording a draw call.
/// </summary>
public bool InCall => _primitiveMode != 0;
/// <summary>
/// Size of one vertex.
/// </summary>
protected int VertexSize => Unsafe.SizeOf<TVertex>();
/// <summary>
/// The list of draw calls.
/// </summary>
protected List<DrawCall> Calls { get; } = new List<DrawCall>();
/// <summary>
/// The list of all vertices.
/// </summary>
protected List<TVertex> Vertices { get; } = new List<TVertex>();
/// <summary>
/// The value to write for the draw call info at the start of a call.
/// </summary>
[Pure] protected virtual TCall DefaultCall => default(TCall);
/// <summary>
/// The value to write for last vertex at the start of a call.
/// </summary>
[Pure] protected virtual TVertex DefaultVertex => default;
private int _start = 0;
private int _count = 0;
private int _primitiveMode = 0;
private TCall _currentCall;
private TVertex _currentVertex;
protected DrawCallRecorder()
{
_currentCall = DefaultCall;
_currentVertex = DefaultVertex;
}
/// <summary>
/// Record a draw call directly.
/// </summary>
/// <param name="type">The primitive type to use.</param>
/// <param name="callInfo">The call info structure to use</param>
/// <param name="vertices">The list of vertices to use.</param>
/// <exception cref="InvalidOperationException">You attempted to use this function during another draw call.</exception>
public void DrawArrays(PrimitiveType type, in TCall callInfo, ReadOnlySpan<TVertex> vertices)
{
if (InCall)
throw new InvalidOperationException("Cannot use draw arrays in the middle of an ongoing immediate-mode call.");
DrawCall call = new DrawCall()
{
Type = type,
Start = Vertices.Count,
Count = vertices.Length,
CallInfo = callInfo,
};
Vertices.AddRange(vertices);
Calls.Add(call);
}
/// <summary>
/// Start a draw call.
/// </summary>
/// <param name="type">The primitive type for the call.</param>
public void Begin(PrimitiveType type) => Begin(type, DefaultCall);
/// <summary>
/// Start a draw call.
/// </summary>
/// <param name="type">The primitive type for the call.</param>
/// <param name="callInfo">The call info.</param>
/// <exception cref="InvalidOperationException">You attempted to create a draw call within a draw call.</exception>
public void Begin(PrimitiveType type, TCall callInfo)
{
if (InCall)
throw new InvalidOperationException("Attempt to begin new draw call before finishing previous one.");
_primitiveMode = (int)type;
_start = Vertices.Count;
_count = 0;
CurrentCall = callInfo;
CurrentVertex = DefaultVertex;
}
/// <summary>
/// Emit the latest or modified vertex.
/// </summary>
public void Vertex()
{
Vertex(CurrentVertex);
}
/// <summary>
/// Emit a vertex.
/// </summary>
/// <param name="vertex">The vertex to emit.</param>
public void Vertex(in TVertex vertex)
{
Vertices.Add(CurrentVertex = vertex);
_count++;
}
/// <summary>
/// End the current call.
/// </summary>
/// <exception cref="InvalidOperationException">You tried to end a call that you didn't begin recording.</exception>
public void End()
{
if (!InCall)
throw new InvalidOperationException("Attempt to end draw call before starting one.");
Calls.Add(new DrawCall()
{
Start = _start,
Count = _count,
Type = (PrimitiveType)_primitiveMode,
CallInfo = CurrentCall,
});
_primitiveMode = 0;
}
/// <summary>
/// Called by the execution engine before a draw call is executed.
/// </summary>
/// <param name="call">The call to prepare.</param>
protected abstract void PrepareCall(in TCall call);
/// <summary>
/// Set the vertex format for the <see cref="Vao"/> and <see cref="Vbo"/> created by the recorder.
/// </summary>
protected abstract void SetVertexFormat();
/// <summary>
/// Execute all the recorded draw calls.
/// </summary>
public void Execute()
{
GL.BindVertexArray(Vao);
GL.BindBuffer(BufferTarget.ArrayBuffer, Vbo);
ReadOnlySpan<TVertex> vertices = CollectionsMarshal.AsSpan(Vertices);
GL.BufferData(BufferTarget.ArrayBuffer, Vertices.Count * VertexSize, vertices, BufferUsage.DynamicDraw);
foreach (DrawCall call in Calls)
{
PrepareCall(call.CallInfo);
GL.DrawArrays(call.Type, call.Start, call.Count);
}
}
/// <summary>
/// Clear the draw call queue.
/// </summary>
public void Clear()
{
Vertices.Clear();
Calls.Clear();
}
public void Dispose()
{
throw new NotImplementedException();
}
public void Dispose(bool safeExit)
{
throw new NotImplementedException();
}
public bool IsInitialized { get; private set; }
public void Initialize()
{
if (IsInitialized)
return;
IsInitialized = true;
Vao = GL.CreateVertexArray();
Vbo = GL.CreateBuffer();
GL.BindVertexArray(Vao);
GL.BindBuffer(BufferTarget.ArrayBuffer, Vbo);
SetVertexFormat();
}
protected struct DrawCall
{
public PrimitiveType Type;
public int Start;
public int Count;
public TCall CallInfo;
}
}
}
@@ -1,333 +0,0 @@
using System.Drawing;
using OpenTK.Graphics.OpenGL;
using System.Numerics;
using Dashboard.OpenGL;
using OTK = OpenTK.Mathematics;
namespace Dashboard.Drawing.OpenGL.Executors
{
public class BaseCommandExecutor : IInitializer, ICommandExecutor
{
private int _program = 0;
private readonly MappableBumpAllocator<CommandInfo> _commands = new MappableBumpAllocator<CommandInfo>();
private readonly DrawCallRecorder _calls = new DrawCallRecorder();
public bool IsInitialized { get; private set; }
public IEnumerable<string> Extensions { get; } = new[] { "DB_base" };
public IContextExecutor Executor { get; private set; } = null!;
public void Initialize()
{
if (IsInitialized) return;
if (Executor == null)
throw new Exception("Executor has not been set.");
IsInitialized = true;
LoadShaders();
}
public void SetContextExecutor(IContextExecutor executor)
{
Executor = executor;
}
public void BeginFrame()
{
}
public void BeginDraw()
{
_commands.Initialize();
_calls.Initialize();
Size size = Executor.Context.FramebufferSize;
Executor.TransformStack.Push(OTK.Matrix4.CreateOrthographicOffCenter(
0,
size.Width,
size.Height,
0,
1,
-1));
GL.Viewport(0, 0, size.Width, size.Height);
}
public void EndDraw()
{
_commands.Unmap();
GL.UseProgram(_program);
_calls.CommandBuffer = _commands.Handle;
_calls.Execute();
}
public void EndFrame()
{
_commands.Clear();
_calls.Clear();
}
public void ProcessCommand(ICommandFrame frame)
{
switch (frame.Command.Name)
{
case "Point":
DrawBasePoint(frame);
break;
case "Line":
DrawBaseLine(frame);
break;
case "RectF":
case "RectS":
case "RectFS":
DrawRect(frame);
break;
}
}
private void DrawBasePoint(ICommandFrame frame)
{
ref CommandInfo info = ref _commands.Take(out int index);
PointCommandArgs args = frame.GetParameter<PointCommandArgs>();
info = new CommandInfo()
{
Type = SimpleDrawCommand.Point,
Arg0 = args.Size,
};
SetCommandCommonBrush(ref info, args.Brush, args.Brush);
_calls.Transforms(Executor.TransformStack.Top);
_calls.Begin(PrimitiveType.Triangles);
_calls.CommandIndex(index);
DrawPoint(args.Position, args.Depth, args.Size);
_calls.End();
}
private void DrawPoint(Vector2 position, float depth, float diameter)
{
// Draw a point as a isocles triangle.
const float adjust = 1.1f;
const float cos30 = 0.8660254038f;
Vector2 top = adjust * new Vector2(0, -cos30);
Vector2 left = adjust * new Vector2(-cos30, 0.5f);
Vector2 right = adjust * new Vector2(cos30, 0.5f);
_calls.TexCoords2(top);
_calls.Vertex3(new Vector3(position + top * diameter, depth));
_calls.TexCoords2(left);
_calls.Vertex3(new Vector3(position + left * diameter, depth));
_calls.TexCoords2(right);
_calls.Vertex3(new Vector3(position + right * diameter, depth));
}
private void DrawBaseLine(ICommandFrame frame)
{
ref CommandInfo info = ref _commands.Take(out int index);
LineCommandArgs args = frame.GetParameter<LineCommandArgs>();
info = new CommandInfo()
{
Type = SimpleDrawCommand.Line,
Arg0 = 0.5f * args.Size / (args.End - args.Start).Length(),
};
SetCommandCommonBrush(ref info, args.Brush, args.Brush);
_calls.Transforms(Executor.TransformStack.Top);
_calls.Begin(PrimitiveType.Triangles);
_calls.CommandIndex(index);
DrawLine(args.Start, args.End, args.Depth, args.Size);
_calls.End();
}
private void DrawLine(Vector2 start, Vector2 end, float depth, float width)
{
float radius = 0.5f * width;
Vector2 segment = end - start;
float length = segment.Length();
float ratio = radius / length;
Vector2 n = ratio * segment;
Vector2 t = new Vector2(-n.Y, n.X);
Vector2 t00 = new Vector2(-ratio, -ratio);
Vector2 t10 = new Vector2(1+ratio, -ratio);
Vector2 t01 = new Vector2(-ratio, +ratio);
Vector2 t11 = new Vector2(1+ratio, +ratio);
Vector3 x00 = new Vector3(start - n - t, depth);
Vector3 x10 = new Vector3(end + n - t, depth);
Vector3 x01 = new Vector3(start - n + t, depth);
Vector3 x11 = new Vector3(end + n + t, depth);
_calls.TexCoords2(t00);
_calls.Vertex3(x00);
_calls.TexCoords2(t01);
_calls.Vertex3(x01);
_calls.TexCoords2(t11);
_calls.Vertex3(x11);
_calls.TexCoords2(t00);
_calls.Vertex3(x00);
_calls.TexCoords2(t11);
_calls.Vertex3(x11);
_calls.TexCoords2(t10);
_calls.Vertex3(x10);
}
private void DrawRect(ICommandFrame frame)
{
ref CommandInfo info = ref _commands.Take(out int index);
RectCommandArgs args = frame.GetParameter<RectCommandArgs>();
Vector2 size = Vector2.Abs(args.End - args.Start);
float aspect = size.X / size.Y;
float border = args.StrikeSize;
float normRad = args.StrikeSize / size.Y;
float wideRad = aspect * normRad;
int flags = 0;
switch (frame.Command.Name)
{
case "RectF":
flags |= 1;
break;
case "RectS":
flags |= 2;
break;
case "RectFS":
flags |= 3;
break;
}
switch (args.BorderKind)
{
case BorderKind.Inset:
flags |= 2 << 2;
break;
case BorderKind.Outset:
flags |= 1 << 2;
break;
}
info = new CommandInfo()
{
Type = SimpleDrawCommand.Rect,
Flags = flags,
Arg0 = aspect,
Arg1 = normRad,
};
SetCommandCommonBrush(ref info, args.FillBrush, args.StrikeBrush);
_calls.Transforms(Executor.TransformStack.Top);
_calls.Begin(PrimitiveType.Triangles);
_calls.CommandIndex(index);
Vector2 t00 = new Vector2(-wideRad, -normRad);
Vector2 t10 = new Vector2(1+wideRad, -normRad);
Vector2 t01 = new Vector2(-wideRad, 1+normRad);
Vector2 t11 = new Vector2(1+wideRad, 1+normRad);
Vector3 x00 = new Vector3(args.Start.X - border, args.Start.Y - border, args.Depth);
Vector3 x10 = new Vector3(args.End.X + border, args.Start.Y - border, args.Depth);
Vector3 x01 = new Vector3(args.Start.X - border, args.End.Y + border, args.Depth);
Vector3 x11 = new Vector3(args.End.X + border, args.End.Y + border, args.Depth);
_calls.TexCoords2(t00);
_calls.Vertex3(x00);
_calls.TexCoords2(t01);
_calls.Vertex3(x01);
_calls.TexCoords2(t11);
_calls.Vertex3(x11);
_calls.TexCoords2(t00);
_calls.Vertex3(x00);
_calls.TexCoords2(t11);
_calls.Vertex3(x11);
_calls.TexCoords2(t10);
_calls.Vertex3(x10);
_calls.End();
}
protected void SetCommandCommonBrush(ref CommandInfo info, IBrush? fill, IBrush? border)
{
switch (fill?.Kind.Name)
{
case "DB_Brush_solid":
SolidBrush solid = (SolidBrush)fill;
Vector4 color = new Vector4(solid.Color.R/255f, solid.Color.G/255f, solid.Color.B/255f, solid.Color.A/255f);
info.FgColor = color;
break;
case "DB_Brush_gradient":
GradientBrush gradient = (GradientBrush)fill;
GradientUniformBuffer gradients = Executor.ResourcePool.GetResourceManager<GradientUniformBuffer>();
gradients.Initialize();
GradientUniformBuffer.Entry entry = gradients.InternGradient(gradient.Gradient);
info.FgGradientIndex = entry.Offset;
info.FgGradientCount = entry.Count;
break;
case null:
// Craete a magenta brush for this.
info.FgColor = new Vector4(1, 0, 1, 1);
break;
}
switch (border?.Kind.Name)
{
case "DB_Brush_solid":
SolidBrush solid = (SolidBrush)border;
Vector4 color = new Vector4(solid.Color.R/255f, solid.Color.G/255f, solid.Color.B/255f, solid.Color.A/255f);
info.BgColor = color;
break;
case "DB_Brush_gradient":
GradientBrush gradient = (GradientBrush)border;
GradientUniformBuffer gradients = Executor.ResourcePool.GetResourceManager<GradientUniformBuffer>();
gradients.Initialize();
GradientUniformBuffer.Entry entry = gradients.InternGradient(gradient.Gradient);
info.BgGradientIndex = entry.Offset;
info.BgGradientCount = entry.Count;
break;
case null:
// Craete a magenta brush for this.
info.BgColor = new Vector4(1, 0, 1, 1);
break;
}
}
private void LoadShaders()
{
using Stream vsource = FetchEmbeddedResource("Dashboard.Drawing.OpenGL.Executors.simple.vert");
using Stream fsource = FetchEmbeddedResource("Dashboard.Drawing.OpenGL.Executors.simple.frag");
int vs = ShaderUtil.CompileShader(ShaderType.VertexShader, vsource);
int fs = ShaderUtil.CompileShader(ShaderType.FragmentShader, fsource);
_program = ShaderUtil.LinkProgram(vs, fs, new []
{
"a_v3Position",
"a_v2TexCoords",
"a_iCmdIndex",
});
GL.DeleteShader(vs);
GL.DeleteShader(fs);
GL.UniformBlockBinding(_program, GL.GetUniformBlockIndex(_program, "CommandBlock"), 0);
}
private static Stream FetchEmbeddedResource(string name)
{
return typeof(BaseCommandExecutor).Assembly.GetManifestResourceStream(name)!;
}
}
}
@@ -1,243 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
using BlurgText;
using Dashboard.OpenGL;
using OpenTK.Graphics.OpenGL;
using OpenTK.Mathematics;
namespace Dashboard.Drawing.OpenGL.Executors
{
public class TextCommandExecutor : ICommandExecutor, IInitializer
{
public IEnumerable<string> Extensions { get; } = new[] { "DB_Text" };
public IContextExecutor Executor { get; private set; }
// private BlurgEngine Engine => Executor.ResourcePool.GetResourceManager<BlurgEngine>();
public bool IsInitialized { get; private set; }
private DrawCallRecorder _recorder;
private int _program = 0;
private int _transformsLocation = -1;
private int _atlasLocation = -1;
private int _borderWidthLocation = -1;
private int _borderColorLocation = -1;
private int _fillColorLocation = -1;
public TextCommandExecutor()
{
Executor = null!;
_recorder = new DrawCallRecorder(this);
}
public void Initialize()
{
if (IsInitialized)
return;
IsInitialized = true;
Assembly self = typeof(TextCommandExecutor).Assembly;
using Stream vsource = self.GetManifestResourceStream("Dashboard.Drawing.OpenGL.Executors.text.vert")!;
using Stream fsource = self.GetManifestResourceStream("Dashboard.Drawing.OpenGL.Executors.text.frag")!;
int vs = ShaderUtil.CompileShader(ShaderType.VertexShader, vsource);
int fs = ShaderUtil.CompileShader(ShaderType.FragmentShader, fsource);
_program = ShaderUtil.LinkProgram(vs, fs, new []
{
"a_v3Position",
"a_v2TexCoords",
});
GL.DeleteShader(vs);
GL.DeleteShader(fs);
_transformsLocation = GL.GetUniformLocation(_program, "m4Transforms");
_atlasLocation = GL.GetUniformLocation(_program, "txAtlas");
_borderWidthLocation = GL.GetUniformLocation(_program, "fBorderWidth");
_borderColorLocation = GL.GetUniformLocation(_program, "v4BorderColor");
_fillColorLocation = GL.GetUniformLocation(_program, "v4FillColor");
_recorder.Initialize();
}
public void SetContextExecutor(IContextExecutor executor)
{
Executor = executor;
}
public void BeginFrame()
{
}
public void BeginDraw()
{
_recorder.Clear();
}
public void EndDraw()
{
GL.UseProgram(_program);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
_recorder.Execute();
GL.Disable(EnableCap.Blend);
}
public void EndFrame()
{
}
public void ProcessCommand(ICommandFrame frame)
{
switch (frame.Command.Name)
{
case "Text":
DrawText(frame);
break;
}
}
private void DrawText(ICommandFrame frame)
{
TextCommandArgs args = frame.GetParameter<TextCommandArgs>();
// DbBlurgFont font = Engine.InternFont(args.Font);
BlurgColor color;
switch (args.TextBrush)
{
case SolidBrush solid:
color = new BlurgColor()
{
R = solid.Color.R,
G = solid.Color.G,
B = solid.Color.B,
A = solid.Color.A,
};
break;
default:
color = new BlurgColor() { R = 255, G = 0, B = 255, A = 255 };
break;
}
//BlurgResult? result = Engine.Blurg.BuildString(font.Font, font.Size, color, args.Text);
// if (result == null)
// return;
//
// Vector3 position = new Vector3(args.Position.X, args.Position.Y, args.Position.Z);
// ExecuteBlurgResult(result, position);
//
// result.Dispose();
}
private void ExecuteBlurgResult(BlurgResult result, Vector3 position)
{
Matrix4 transforms = Executor.TransformStack.Top;
for (int i = 0; i < result.Count; i++)
{
BlurgRect rect = result[i];
int texture = (int)rect.UserData;
Vector4 color = new Vector4(rect.Color.R / 255f, rect.Color.G / 255f, rect.Color.B / 255f,
rect.Color.A / 255f);
if (i == 0)
{
_recorder.Begin(PrimitiveType.Triangles, new Call()
{
Texture = texture,
FillColor = color,
Transforms = transforms,
});
}
else if (
_recorder.CurrentCall.Texture != texture ||
_recorder.CurrentCall.FillColor != color)
{
_recorder.End();
Call call = new Call()
{
Texture = texture,
FillColor = color,
Transforms = transforms,
};
_recorder.Begin(PrimitiveType.Triangles, call);
}
Vector3 p00 = new Vector3(rect.X, rect.Y, 0) + position;
Vector3 p10 = p00 + new Vector3(rect.Width, 0, 0);
Vector3 p11 = p00 + new Vector3(rect.Width, rect.Height, 0);
Vector3 p01 = p00 + new Vector3(0, rect.Height, 0);
Vector2 uv00 = new Vector2(rect.U0, rect.V0);
Vector2 uv10 = new Vector2(rect.U1, rect.V0);
Vector2 uv11 = new Vector2(rect.U1, rect.V1);
Vector2 uv01 = new Vector2(rect.U0, rect.V1);
_recorder.Vertex(p00, uv00);
_recorder.Vertex(p10, uv10);
_recorder.Vertex(p11, uv11);
_recorder.Vertex(p00, uv00);
_recorder.Vertex(p11, uv11);
_recorder.Vertex(p01, uv01);
}
_recorder.End();
}
private struct Call
{
public Matrix4 Transforms = Matrix4.Identity;
public int Texture = 0;
public float BorderWidth = 0f;
public Vector4 FillColor = Vector4.One;
public Vector4 BorderColor = new Vector4(0,0,0,1);
public Call()
{
}
}
[StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(float))]
private struct Vertex
{
[FieldOffset(0)]
public Vector3 Position;
[FieldOffset(4 * sizeof(float))]
public Vector2 TexCoords;
}
private class DrawCallRecorder : DrawCallRecorder<Call, Vertex>
{
private TextCommandExecutor Executor { get; }
public DrawCallRecorder(TextCommandExecutor executor)
{
Executor = executor;
}
public void Vertex(Vector3 position, Vector2 texCoords)
{
Vertex(new Vertex(){Position = position, TexCoords = texCoords});
}
protected override void PrepareCall(in Call call)
{
Matrix4 transforms = call.Transforms;
GL.UniformMatrix4f(Executor._transformsLocation, 1, true, ref transforms);
GL.Uniform1f(Executor._borderWidthLocation, call.BorderWidth);
GL.Uniform4f(Executor._borderColorLocation, 1, in call.BorderColor);
GL.Uniform4f(Executor._fillColorLocation, 1, in call.FillColor);
GL.Uniform1i(Executor._atlasLocation, 0);
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2d, call.Texture);
}
protected override void SetVertexFormat()
{
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, VertexSize, 0);
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, VertexSize, 4*sizeof(float));
GL.EnableVertexAttribArray(0);
GL.EnableVertexAttribArray(1);
}
}
}
}
@@ -1,55 +0,0 @@
#ifndef _GRADIENT_GLSL_
#define _GRADIENT_GLSL_
#define DB_GRADIENT_MAX 16
struct Gradient_t {
float fPosition;
float pad0;
float pad1;
float pad2;
vec4 v4Color;
};
uniform GradientBlock
{
Gradient_t vstGradientStops[DB_GRADIENT_MAX];
};
vec4 getGradientColor(float position, int index, int count)
{
position = clamp(position, 0, 1);
int i0 = 0;
float p0 = vstGradientStops[index + i0].fPosition;
int i1 = count - 1;
float p1 = vstGradientStops[index + i1].fPosition;
for (int i = 0; i < count; i++)
{
float px = vstGradientStops[index + i].fPosition;
if (px > p0 && px <= position)
{
p0 = px;
i0 = i;
}
if (px < p1 && px >= position)
{
p1 = px;
i1 = i;
}
}
vec4 c0 = vstGradientStops[index + i0].v4Color;
vec4 c1 = vstGradientStops[index + i1].v4Color;
float l = p1 - p0;
float w = (l > 0) ? (position - p0) / (p1 - p0) : 0;
return mix(c0, c1, w);
}
#endif
@@ -1,224 +0,0 @@
#version 140
#define DB_GRADIENT_MAX 16
#define DB_COMMAND_MAX 64
#define CMD_POINT 1
#define CMD_LINE 2
#define CMD_RECT 3
#define STRIKE_CENTER 0
#define STRIKE_OUTSET 1
#define STRIKE_INSET 2
in vec3 v_v3Position;
in vec2 v_v2TexCoords;
flat in int v_iCmdIndex;
out vec4 f_Color;
uniform sampler2D txForeground;
uniform sampler2D txBackground;
struct Gradient_t {
float fPosition;
float pad0;
float pad1;
float pad2;
vec4 v4Color;
};
uniform GradientBlock
{
Gradient_t vstGradientStops[DB_GRADIENT_MAX];
};
vec4 getGradientColor(float position, int index, int count)
{
position = clamp(position, 0, 1);
int i0 = 0;
float p0 = vstGradientStops[index + i0].fPosition;
int i1 = count - 1;
float p1 = vstGradientStops[index + i1].fPosition;
for (int i = 0; i < count; i++)
{
float px = vstGradientStops[index + i].fPosition;
if (px > p0 && px <= position)
{
p0 = px;
i0 = i;
}
if (px < p1 && px >= position)
{
p1 = px;
i1 = i;
}
}
vec4 c0 = vstGradientStops[index + i0].v4Color;
vec4 c1 = vstGradientStops[index + i1].v4Color;
float l = p1 - p0;
float w = (l > 0) ? (position - p0) / (p1 - p0) : 0;
return mix(c0, c1, w);
}
struct CommandInfo_t {
int iCommand;
int iFlags;
float fArg0;
float fArg1;
int iFgGradientIndex;
int iFgGradientCount;
int iBgGradientIndex;
int iBgGradientCount;
vec4 v4FgColor;
vec4 v4BgColor;
};
uniform CommandBlock
{
CommandInfo_t vstCommandInfo[DB_COMMAND_MAX];
};
CommandInfo_t getCommandInfo()
{
return vstCommandInfo[v_iCmdIndex];
}
vec4 fgColor()
{
return getCommandInfo().v4FgColor;
}
vec4 bgColor()
{
return getCommandInfo().v4BgColor;
}
void Point(void)
{
vec4 fg = fgColor();
if (dot(v_v2TexCoords, v_v2TexCoords) <= 0.25)
f_Color = fg;
else
discard;
}
#define LINE_NORMALIZED_RADIUS(cmd) cmd.fArg0
void Line(void)
{
vec4 fg = fgColor();
CommandInfo_t cmd = getCommandInfo();
float t = clamp(v_v2TexCoords.x, 0, 1);
vec2 dv = v_v2TexCoords - vec2(t, 0);
float d = dot(dv, dv);
float lim = LINE_NORMALIZED_RADIUS(cmd);
lim *= lim;
if (d <= lim)
f_Color = fg;
else
discard;
}
#define RECT_ASPECT_RATIO(cmd) (cmd.fArg0)
#define RECT_BORDER_WIDTH(cmd) (cmd.fArg1)
#define RECT_FILL(cmd) ((cmd.iFlags & (1 << 0)) != 0)
#define RECT_BORDER(cmd) ((cmd.iFlags & (1 << 1)) != 0)
#define RECT_STRIKE_MASK 3
#define RECT_STRIKE_SHIFT 2
#define RECT_STRIKE_KIND(cmd) ((cmd.iFlags & RECT_STRIKE_MASK) >> RECT_STRIKE_SHIFT)
void Rect(void)
{
vec4 fg = fgColor();
vec4 bg = bgColor();
CommandInfo_t cmd = getCommandInfo();
float aspect = RECT_ASPECT_RATIO(cmd);
float border = RECT_BORDER_WIDTH(cmd);
int strikeKind = RECT_STRIKE_KIND(cmd);
vec2 p = abs(2*v_v2TexCoords - vec2(1));
p.x = p.x/aspect;
float m0;
float m1;
if (!RECT_BORDER(cmd))
{
m0 = 1;
m1 = 1;
}
else if (strikeKind == STRIKE_OUTSET)
{
m0 = 1;
m1 = border;
}
else if (strikeKind == STRIKE_INSET)
{
m0 = 1-border;
m1 = 1;
}
else // strikeKind == STRIKE_CENTER
{
float h = 0.5 * border;
m0 = 1-border;
m1 = 1+border;
}
if (p.x > m1*aspect || p.y > m1)
{
discard;
}
if (RECT_FILL(cmd))
{
if (p.x <= 1 && p.y <= 1)
{
f_Color = fg;
}
}
if (RECT_BORDER(cmd))
{
float x = clamp(p.x, aspect*m0, aspect*m1);
float y = clamp(p.y, m0, m1);
if (p.x == x || p.y == y)
{
f_Color = bg;
}
}
}
void main(void)
{
switch (getCommandInfo().iCommand)
{
case CMD_POINT:
Point();
break;
case CMD_LINE:
Line();
break;
case CMD_RECT:
Rect();
break;
default:
// Unimplemented value.
f_Color = vec4(1, 0, 1, 1);
break;
}
}
@@ -1,21 +0,0 @@
#version 140
in vec3 a_v3Position;
in vec2 a_v2TexCoords;
in int a_iCmdIndex;
out vec3 v_v3Position;
out vec2 v_v2TexCoords;
flat out int v_iCmdIndex;
uniform mat4 m4Transforms;
void main(void)
{
vec4 position = vec4(a_v3Position, 1) * m4Transforms;
gl_Position = position;
v_v3Position = position.xyz/position.w;
v_v2TexCoords = a_v2TexCoords;
v_iCmdIndex = a_iCmdIndex;
}
@@ -1,21 +0,0 @@
#version 140
in vec3 v_v3Position;
in vec2 v_v2TexCoords;
out vec4 f_Color;
uniform sampler2D txAtlas;
uniform float fBorderWidth;
uniform vec4 v4BorderColor;
uniform vec4 v4FillColor;
void main() {
// For now just honor the fill color
vec4 color = texture(txAtlas, v_v2TexCoords) * v4FillColor;
if (color.a <= 0.1)
discard;
f_Color = color;
}
@@ -1,18 +0,0 @@
#version 140
in vec3 a_v3Position;
in vec2 a_v2TexCoords;
out vec3 v_v3Position;
out vec2 v_v2TexCoords;
uniform mat4 m4Transforms;
void main(void)
{
vec4 position = vec4(a_v3Position, 1) * m4Transforms;
gl_Position = position;
v_v3Position = position.xyz/position.w;
v_v2TexCoords = a_v2TexCoords;
}
-40
View File
@@ -1,40 +0,0 @@
// using Dashboard.Drawing.OpenGL.Text;
using Dashboard.OpenGL;
using OpenTK;
using OpenTK.Graphics;
namespace Dashboard.Drawing.OpenGL
{
public class GLEngine
{
private readonly Dictionary<IGLContext, ContextExecutor> _executors = new Dictionary<IGLContext, ContextExecutor>();
public bool IsInitialized { get; private set; } = false;
public ContextResourcePoolManager ResourcePoolManager { get; private set; } = new ContextResourcePoolManager();
public void Initialize(IBindingsContext? bindingsContext = null)
{
if (IsInitialized)
return;
IsInitialized = true;
if (bindingsContext != null)
GLLoader.LoadBindings(bindingsContext);
// Typesetter.Backend = BlurgEngine.Global;
}
public ContextExecutor GetExecutor(IGLContext glContext)
{
if (!_executors.TryGetValue(glContext, out ContextExecutor? executor))
{
executor = new ContextExecutor(this, glContext);
executor.Initialize();
_executors.Add(glContext, executor);
}
return executor;
}
}
}
@@ -1,90 +0,0 @@
using System.Runtime.InteropServices;
using Dashboard.OpenGL;
using OpenTK.Mathematics;
namespace Dashboard.Drawing.OpenGL
{
public class GradientUniformBuffer : IInitializer, IGLDisposable, IResourceManager
{
private bool _isDisposed;
private int _top = 0;
private readonly MappableBuffer<GradientUniformStruct> _buffer = new MappableBuffer<GradientUniformStruct>();
private readonly Dictionary<Gradient, Entry> _entries = new Dictionary<Gradient, Entry>();
public bool IsInitialized { get; private set; } = false;
public void Initialize()
{
if (IsInitialized)
return;
IsInitialized = true;
_buffer.Initialize();
}
public Entry InternGradient(Gradient gradient)
{
if (_entries.TryGetValue(gradient, out Entry entry))
return entry;
int count = gradient.Count;
int offset = _top;
_top += count;
_buffer.EnsureCapacity(_top);
_buffer.Map();
Span<GradientUniformStruct> span = _buffer.AsSpan()[offset.._top];
for (int i = 0; i < count; i++)
{
GradientStop stop = gradient[i];
span[i] = new GradientUniformStruct()
{
Position = stop.Position,
Color = new Vector4(
stop.Color.R / 255f,
stop.Color.G / 255f,
stop.Color.B / 255f,
stop.Color.A / 255f),
};
}
entry = new Entry(offset, count);
_entries.Add(gradient, entry);
return entry;
}
public void Clear()
{
_entries.Clear();
_top = 0;
}
public record struct Entry(int Offset, int Count);
public void Dispose() => Dispose(true);
public void Dispose(bool safeExit)
{
if (_isDisposed)
return;
_isDisposed = true;
_buffer.Dispose(safeExit);
}
string IResourceManager.Name { get; } = nameof(GradientUniformBuffer);
}
[StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(float))]
public struct GradientUniformStruct
{
[FieldOffset(0 * sizeof(float))]
public float Position;
[FieldOffset(4 * sizeof(float))]
public Vector4 Color;
}
}
-24
View File
@@ -1,24 +0,0 @@
namespace Dashboard.Drawing.OpenGL
{
/// <summary>
/// Atomic reference counter.
/// </summary>
public interface IArc : IDisposable
{
/// <summary>
/// The number of references to this.
/// </summary>
int References { get; }
/// <summary>
/// Increment the number of references.
/// </summary>
void IncrementReference();
/// <summary>
/// Decrement the number of references.
/// </summary>
/// <returns>True if this was the last reference.</returns>
bool DecrementReference();
}
}
-9
View File
@@ -1,9 +0,0 @@
namespace Dashboard.Drawing.OpenGL
{
public interface IInitializer
{
bool IsInitialized { get; }
void Initialize();
}
}
@@ -1,7 +0,0 @@
namespace Dashboard.Drawing.OpenGL
{
public interface IResourceManager
{
public string Name { get; }
}
}
-168
View File
@@ -1,168 +0,0 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using Dashboard.OpenGL;
using OpenTK.Graphics.OpenGL;
namespace Dashboard.Drawing.OpenGL
{
public class MappableBuffer<T> : IInitializer, IGLDisposable where T : struct
{
public int Handle { get; private set; } = 0;
public int Capacity { get; set; } = BASE_CAPACITY;
public IntPtr Pointer { get; private set; } = IntPtr.Zero;
public bool IsInitialized => Handle != 0;
private bool _isDisposed = false;
private const int BASE_CAPACITY = 4 << 10; // 4 KiB
private const int MAX_INCREMENT = 4 << 20; // 4 MiB
~MappableBuffer()
{
Dispose(true, false);
}
public void Initialize()
{
if (IsInitialized)
return;
Handle = GL.GenBuffer();
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
GL.BufferData(BufferTarget.ArrayBuffer, Capacity, IntPtr.Zero, BufferUsage.DynamicDraw);
}
public void EnsureCapacity(int count)
{
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count));
if (Capacity > count)
return;
SetSize(count, false);
}
public void SetSize(int count, bool clear = false)
{
AssertInitialized();
Unmap();
int sz = Unsafe.SizeOf<T>();
int oldsize = Capacity * sz;
int request = count * sz;
int newsize;
if (request < BASE_CAPACITY)
request = BASE_CAPACITY;
if (request > MAX_INCREMENT)
{
newsize = ((request + MAX_INCREMENT - 1) / MAX_INCREMENT) * MAX_INCREMENT;
}
else
{
newsize = checked((int)BitOperations.RoundUpToPowerOf2((ulong)request));
}
int dest = GL.GenBuffer();
if (clear)
{
GL.BindBuffer(BufferTarget.ArrayBuffer, dest);
GL.BufferData(BufferTarget.ArrayBuffer, newsize, IntPtr.Zero, BufferUsage.DynamicDraw);
}
else
{
GL.BindBuffer(BufferTarget.CopyWriteBuffer, dest);
GL.BindBuffer(BufferTarget.CopyReadBuffer, Handle);
GL.BufferData(BufferTarget.CopyWriteBuffer, newsize, IntPtr.Zero, BufferUsage.DynamicDraw);
GL.CopyBufferSubData(CopyBufferSubDataTarget.CopyReadBuffer, CopyBufferSubDataTarget.CopyWriteBuffer, 0, 0, Math.Min(newsize, oldsize));
}
GL.DeleteBuffer(Handle);
Handle = dest;
Capacity = newsize / Unsafe.SizeOf<T>();
}
public unsafe void Map()
{
if (Pointer != IntPtr.Zero)
return;
AssertInitialized();
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
Pointer = (IntPtr)GL.MapBuffer(BufferTarget.ArrayBuffer, BufferAccess.ReadWrite);
}
public void Unmap()
{
if (Pointer == IntPtr.Zero)
return;
AssertInitialized();
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
GL.UnmapBuffer(BufferTarget.ArrayBuffer);
Pointer = IntPtr.Zero;
}
public unsafe Span<T> AsSpan()
{
if (Pointer == IntPtr.Zero)
throw new InvalidOperationException("The buffer is not currently mapped.");
AssertInitialized();
return new Span<T>(Pointer.ToPointer(), Capacity);
}
private void AssertInitialized()
{
if (Handle == 0)
throw new InvalidOperationException("The buffer is not initialized.");
}
private void Dispose(bool safeExit, bool disposing)
{
if (_isDisposed)
return;
_isDisposed = true;
if (disposing)
GC.SuppressFinalize(this);
if (safeExit)
ContextCollector.Global.DeleteBufffer(Handle);
}
public void Dispose() => Dispose(true, true);
public void Dispose(bool safeExit) => Dispose(safeExit, true);
}
public class MappableBumpAllocator<T> : MappableBuffer<T> where T : struct
{
private int _top = 0;
private int _previousTop = 0;
public ref T Take(out int index)
{
index = _top;
EnsureCapacity(++_top);
Map();
return ref AsSpan()[index];
}
public ref T Take() => ref Take(out _);
public void Clear()
{
SetSize(0, true);
_previousTop = _top;
_top = 0;
}
}
}
@@ -1,51 +0,0 @@
using OpenTK.Mathematics;
namespace Dashboard.Drawing.OpenGL
{
/// <summary>
/// The current stack of transformations.
/// </summary>
public class TransformStack
{
private Matrix4 _top = Matrix4.Identity;
private readonly Stack<Matrix4> _stack = new Stack<Matrix4>();
/// <summary>
/// The top-most transform matrix.
/// </summary>
public ref readonly Matrix4 Top => ref _top;
/// <summary>
/// The number of matrices in the stack.
/// </summary>
public int Count => _stack.Count;
/// <summary>
/// Push a transform.
/// </summary>
/// <param name="transform">The transform to push.</param>
public void Push(in Matrix4 transform)
{
_stack.Push(_top);
_top = transform * _top;
}
/// <summary>
/// Pop a transform.
/// </summary>
public void Pop()
{
if (!_stack.TryPop(out _top))
_top = Matrix4.Identity;
}
/// <summary>
/// Clear the stack of transformations.
/// </summary>
public void Clear()
{
_stack.Clear();
_top = Matrix4.Identity;
}
}
}
-52
View File
@@ -1,52 +0,0 @@
using System;
using System.Drawing;
namespace Dashboard.Drawing
{
public class BrushExtension : DrawExtension
{
private BrushExtension() : base("DB_Brush") { }
public static readonly BrushExtension Instance = new BrushExtension();
}
public interface IBrush : IDrawResource
{
}
public readonly struct SolidBrush(Color color) : IBrush
{
public IDrawExtension Kind { get; } = SolidBrushExtension.Instance;
public Color Color { get; } = color;
public override int GetHashCode()
{
return HashCode.Combine(Kind, Color);
}
}
public readonly struct GradientBrush(Gradient gradient) : IBrush
{
public IDrawExtension Kind { get; } = GradientBrushExtension.Instance;
public Gradient Gradient { get; } = gradient;
public override int GetHashCode()
{
return HashCode.Combine(Kind, Gradient);
}
}
public class SolidBrushExtension : DrawExtension
{
private SolidBrushExtension() : base("DB_Brush_solid", new[] { BrushExtension.Instance }) { }
public static readonly SolidBrushExtension Instance = new SolidBrushExtension();
}
public class GradientBrushExtension : DrawExtension
{
private GradientBrushExtension() : base("DB_Brush_gradient", new[] { BrushExtension.Instance }) { }
public static readonly GradientBrushExtension Instance = new GradientBrushExtension();
}
}
@@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Dashboard.Common\Dashboard.Common.csproj" />
</ItemGroup>
</Project>
-292
View File
@@ -1,292 +0,0 @@
using System;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Dashboard.Drawing
{
public class DbBaseCommands : DrawExtension
{
public DrawCommand<PointCommandArgs> DrawPoint { get; }
public DrawCommand<LineCommandArgs> DrawLine { get; }
public RectCommand DrawRectF { get; }
public RectCommand DrawRectS { get; }
public RectCommand DrawRectFS { get; }
private DbBaseCommands() : base("DB_base",
new[]
{
BrushExtension.Instance,
})
{
AddCommand(DrawPoint = new DrawCommand<PointCommandArgs>("Point", this, PointCommandArgs.CommandSize));
AddCommand(DrawLine = new DrawCommand<LineCommandArgs>("Line", this, LineCommandArgs.CommandSize));
AddCommand(DrawRectF = new RectCommand(this, RectCommand.Mode.Fill));
AddCommand(DrawRectS = new RectCommand(this, RectCommand.Mode.Strike));
AddCommand(DrawRectFS = new RectCommand(this, RectCommand.Mode.FillStrike));
}
public static readonly DbBaseCommands Instance = new DbBaseCommands();
}
public struct PointCommandArgs : IParameterSerializer<PointCommandArgs>
{
public Vector2 Position { get; private set; }
public float Depth { get; private set; }
public float Size { get; private set; }
public IBrush? Brush { get; private set; }
public PointCommandArgs(Vector2 position, float depth, float size, IBrush brush)
{
Position = position;
Depth = depth;
Brush = brush;
Size = size;
}
public int Serialize(DrawQueue queue, Span<byte> bytes)
{
if (bytes.Length < CommandSize)
return CommandSize;
Span<Value> value = stackalloc Value[]
{
new Value(Position, Depth, Size, queue.RequireResource(Brush!))
};
MemoryMarshal.AsBytes(value).CopyTo(bytes);
return CommandSize;
}
[MemberNotNull(nameof(Brush))]
public void Deserialize(DrawQueue queue, ReadOnlySpan<byte> bytes)
{
if (bytes.Length < CommandSize)
throw new Exception("Not enough bytes");
Value value = MemoryMarshal.AsRef<Value>(bytes);
Position = value.Position;
Depth = value.Depth;
Size = value.Size;
Brush = (IBrush)queue.Resources[value.BrushIndex];
}
private record struct Value(Vector2 Position, float Depth, float Size, int BrushIndex);
public static readonly int CommandSize = Unsafe.SizeOf<Value>();
}
public struct LineCommandArgs : IParameterSerializer<LineCommandArgs>
{
public Vector2 Start { get; private set; }
public Vector2 End { get; private set; }
public float Depth { get; private set; }
public float Size { get; private set; }
public IBrush? Brush { get; private set; }
public LineCommandArgs(Vector2 start, Vector2 end, float depth, float size, IBrush brush)
{
Start = start;
End = end;
Depth = depth;
Size = size;
Brush = brush;
}
public int Serialize(DrawQueue queue, Span<byte> bytes)
{
if (bytes.Length < CommandSize)
return CommandSize;
Span<Value> value = stackalloc Value[]
{
new Value(Start, End, Depth, Size, queue.RequireResource(Brush!))
};
MemoryMarshal.AsBytes(value).CopyTo(bytes);
return CommandSize;
}
public void Deserialize(DrawQueue queue, ReadOnlySpan<byte> bytes)
{
if (bytes.Length < CommandSize)
throw new Exception("Not enough bytes");
Value value = MemoryMarshal.AsRef<Value>(bytes);
Start = value.Start;
End = value.End;
Depth = value.Depth;
Size = value.Size;
Brush = (IBrush)queue.Resources[value.BrushIndex];
}
private record struct Value(Vector2 Start, Vector2 End, float Depth, float Size, int BrushIndex);
public static readonly int CommandSize = Unsafe.SizeOf<Value>();
}
public class RectCommand : IDrawCommand<RectCommandArgs>
{
private readonly Mode _mode;
public string Name { get; }
public IDrawExtension Extension { get; }
public int Length { get; }
public RectCommand(IDrawExtension extension, Mode mode)
{
Extension = extension;
_mode = mode;
switch (mode)
{
case Mode.Fill:
Name = "RectF";
Length = Unsafe.SizeOf<RectF>();
break;
case Mode.Strike:
Name = "RectS";
Length = Unsafe.SizeOf<RectS>();
break;
default:
Name = "RectFS";
Length = Unsafe.SizeOf<RectFS>();
break;
}
}
object? IDrawCommand.GetParams(DrawQueue queue, ReadOnlySpan<byte> param)
{
return GetParams(queue, param);
}
public RectCommandArgs GetParams(DrawQueue queue, ReadOnlySpan<byte> param)
{
if (param.Length < Length)
throw new Exception("Not enough bytes");
RectCommandArgs args;
switch (_mode)
{
case Mode.Fill:
ref readonly RectF f = ref MemoryMarshal.AsRef<RectF>(param);
args = new RectCommandArgs(f.Start, f.End, f.Depth, (IBrush)queue.Resources[f.FillBrushIndex]);
break;
case Mode.Strike:
ref readonly RectS s = ref MemoryMarshal.AsRef<RectS>(param);
args = new RectCommandArgs(s.Start, s.End, s.Depth, (IBrush)queue.Resources[s.StrikeBrushIndex], s.StrikeSize, s.BorderKind);
break;
default:
ref readonly RectFS fs = ref MemoryMarshal.AsRef<RectFS>(param);
args = new RectCommandArgs(fs.Start, fs.End, fs.Depth, (IBrush)queue.Resources[fs.FillBrushIndex],
(IBrush)queue.Resources[fs.StrikeBrushIndex], fs.StrikeSize, fs.BorderKind);
break;
}
return args;
}
public int WriteParams(DrawQueue queue, object? obj, Span<byte> param)
{
return WriteParams(queue, (RectCommandArgs)obj, param);
}
public int WriteParams(DrawQueue queue, RectCommandArgs obj, Span<byte> param)
{
if (param.Length < Length)
return Length;
switch (_mode)
{
case Mode.Fill:
ref RectF f = ref MemoryMarshal.AsRef<RectF>(param);
f.Start = obj.Start;
f.End = obj.End;
f.Depth = obj.Depth;
f.FillBrushIndex = queue.RequireResource(obj.FillBrush!);
break;
case Mode.Strike:
ref RectS s = ref MemoryMarshal.AsRef<RectS>(param);
s.Start = obj.Start;
s.End = obj.End;
s.Depth = obj.Depth;
s.StrikeBrushIndex = queue.RequireResource(obj.StrikeBrush!);
s.StrikeSize = obj.StrikeSize;
s.BorderKind = obj.BorderKind;
break;
default:
ref RectFS fs = ref MemoryMarshal.AsRef<RectFS>(param);
fs.Start = obj.Start;
fs.End = obj.End;
fs.Depth = obj.Depth;
fs.FillBrushIndex = queue.RequireResource(obj.FillBrush!);
fs.StrikeBrushIndex = queue.RequireResource(obj.StrikeBrush!);
fs.StrikeSize = obj.StrikeSize;
fs.BorderKind = obj.BorderKind;
break;
}
return Length;
}
[Flags]
public enum Mode
{
Fill = 1,
Strike = 2,
FillStrike = Fill | Strike,
}
private record struct RectF(Vector2 Start, Vector2 End, float Depth, int FillBrushIndex);
private record struct RectS(Vector2 Start, Vector2 End, float Depth, int StrikeBrushIndex, float StrikeSize, BorderKind BorderKind);
private record struct RectFS(Vector2 Start, Vector2 End, float Depth, int FillBrushIndex, int StrikeBrushIndex, float StrikeSize, BorderKind BorderKind);
}
public struct RectCommandArgs
{
public Vector2 Start { get; private set; }
public Vector2 End { get; private set; }
public float Depth { get; private set; }
public float StrikeSize { get; private set; } = 0f;
public BorderKind BorderKind { get; private set; } = BorderKind.Center;
public IBrush? FillBrush { get; private set; } = null;
public IBrush? StrikeBrush { get; private set; } = null;
public bool IsStruck => StrikeSize != 0;
public RectCommandArgs(Vector2 start, Vector2 end, float depth, IBrush fillBrush)
{
Start = start;
End = end;
Depth = depth;
FillBrush = fillBrush;
}
public RectCommandArgs(Vector2 start, Vector2 end, float depth, IBrush strikeBrush, float strikeSize, BorderKind borderKind)
{
Start = start;
End = end;
Depth = depth;
StrikeBrush = strikeBrush;
StrikeSize = strikeSize;
BorderKind = borderKind;
}
public RectCommandArgs(Vector2 start, Vector2 end, float depth, IBrush fillBrush, IBrush strikeBrush, float strikeSize,
BorderKind borderKind)
{
Start = start;
End = end;
Depth = depth;
FillBrush = fillBrush;
StrikeBrush = strikeBrush;
StrikeSize = strikeSize;
BorderKind = borderKind;
}
}
}
-26
View File
@@ -1,26 +0,0 @@
using System.Numerics;
namespace Dashboard.Drawing
{
public enum DrawPrimitive
{
Point,
Line,
LineStrip,
Triangle,
TriangleFan,
TriangleStrip
}
public record struct DrawVertex(Vector3 Position, Vector3 TextureCoordinate, Vector4 Color);
public record DrawInfo(DrawPrimitive Primitive, int Count)
{
}
public class DrawBuffer
{
}
}
-107
View File
@@ -1,107 +0,0 @@
using System;
namespace Dashboard.Drawing
{
public interface IDrawCommand
{
/// <summary>
/// Name of the command.
/// </summary>
string Name { get; }
/// <summary>
/// The draw extension that defines this command.
/// </summary>
IDrawExtension Extension { get; }
/// <summary>
/// The length of the command data segment, in bytes.
/// </summary>
/// <remarks>
/// Must be 0 for simple commands. For commands that are variadic, the
/// value must be less than 0. Any other positive value, otherwise.
/// </remarks>
int Length { get; }
/// <summary>
/// Get the parameters object for this command.
/// </summary>
/// <param name="param">The parameter array.</param>
/// <returns>The parameters object.</returns>
object? GetParams(DrawQueue queue, ReadOnlySpan<byte> param);
int WriteParams(DrawQueue queue, object? obj, Span<byte> param);
}
public interface IDrawCommand<T> : IDrawCommand
{
/// <summary>
/// Get the parameters object for this command.
/// </summary>
/// <param name="param">The parameter array.</param>
/// <returns>The parameters object.</returns>
new T? GetParams(DrawQueue queue, ReadOnlySpan<byte> param);
new int WriteParams(DrawQueue queue, T? obj, Span<byte> param);
}
public sealed class DrawCommand : IDrawCommand
{
public string Name { get; }
public IDrawExtension Extension { get; }
public int Length { get; } = 0;
public DrawCommand(string name, IDrawExtension extension)
{
Name = name;
Extension = extension;
}
public object? GetParams(DrawQueue queue, ReadOnlySpan<byte> param)
{
return null;
}
public int WriteParams(DrawQueue queue, object? obj, Span<byte> param)
{
return 0;
}
}
public sealed class DrawCommand<T> : IDrawCommand<T>
where T : IParameterSerializer<T>, new()
{
public string Name { get; }
public IDrawExtension Extension { get; }
public int Length { get; }
public DrawCommand(string name, IDrawExtension extension, int length)
{
Name = name;
Extension = extension;
Length = length;
}
public T? GetParams(DrawQueue queue, ReadOnlySpan<byte> param)
{
T t = new T();
t.Deserialize(queue, param);
return t;
}
public int WriteParams(DrawQueue queue, T? obj, Span<byte> param)
{
return obj!.Serialize(queue, param);
}
int IDrawCommand.WriteParams(DrawQueue queue, object? obj, Span<byte> param)
{
return WriteParams(queue, (T?)obj, param);
}
object? IDrawCommand.GetParams(DrawQueue queue, ReadOnlySpan<byte> param)
{
return GetParams(queue, param);
}
}
}
-141
View File
@@ -1,141 +0,0 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Drawing;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
namespace Dashboard.Drawing
{
/// <summary>
/// Interface for all drawing extensions.
/// </summary>
public interface IDrawExtension
{
/// <summary>
/// Name of this extension.
/// </summary>
public string Name { get; }
public IReadOnlyList<IDrawExtension> Requires { get; }
/// <summary>
/// The list of commands this extension defines, if any.
/// </summary>
public IReadOnlyList<IDrawCommand> Commands { get; }
}
/// <summary>
/// A simple draw extension.
/// </summary>
public class DrawExtension : IDrawExtension
{
private readonly List<IDrawCommand> _drawCommands = new List<IDrawCommand>();
public string Name { get; }
public IReadOnlyList<IDrawCommand> Commands { get; }
public IReadOnlyList<IDrawExtension> Requires { get; }
public DrawExtension(string name, IEnumerable<IDrawExtension>? requires = null)
{
Name = name;
Commands = _drawCommands.AsReadOnly();
Requires = (requires ?? Enumerable.Empty<IDrawExtension>()).ToImmutableList();
}
protected void AddCommand(IDrawCommand command)
{
_drawCommands.Add(command);
}
}
public static class DrawExtensionClass
{
/// <summary>
/// Get the draw controller for the given queue.
/// </summary>
/// <param name="extension">The extension instance.</param>
/// <param name="queue">The draw queue.</param>
/// <returns>The draw controller for this queue.</returns>
public static IDrawController GetController(this IDrawExtension extension, DrawQueue queue)
{
return queue.GetController(extension);
}
public static void Point(this DrawQueue queue, Vector2 position, float depth, float size, IBrush brush)
{
Vector2 radius = new Vector2(0.5f * size);
Box2d bounds = new Box2d(position - radius, position + radius);
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
controller.EnsureBounds(bounds, depth);
controller.Write(DbBaseCommands.Instance.DrawPoint, new PointCommandArgs(position, depth, size, brush));
}
public static void Line(this DrawQueue queue, Vector2 start, Vector2 end, float depth, float size, IBrush brush)
{
Vector2 radius = new Vector2(size / 2f);
Vector2 min = Vector2.Min(start, end) - radius;
Vector2 max = Vector2.Max(start, end) + radius;
Box2d bounds = new Box2d(min, max);
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
controller.EnsureBounds(bounds, depth);
controller.Write(DbBaseCommands.Instance.DrawLine, new LineCommandArgs(start, end, depth, size, brush));
}
public static void Rect(this DrawQueue queue, Vector2 start, Vector2 end, float depth, IBrush fillBrush)
{
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
Vector2 min = Vector2.Min(start, end);
Vector2 max = Vector2.Max(start, end);
controller.EnsureBounds(new Box2d(min, max), depth);
controller.Write(DbBaseCommands.Instance.DrawRectF, new RectCommandArgs(start, end, depth, fillBrush));
}
public static void Rect(this DrawQueue queue, Vector2 start, Vector2 end, float depth, IBrush strikeBrush, float strikeSize,
BorderKind kind = BorderKind.Center)
{
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
Vector2 min = Vector2.Min(start, end);
Vector2 max = Vector2.Max(start, end);
controller.EnsureBounds(new Box2d(min, max), depth);
controller.Write(DbBaseCommands.Instance.DrawRectS, new RectCommandArgs(start, end, depth, strikeBrush, strikeSize, kind));
}
public static void Rect(this DrawQueue queue, Vector2 start, Vector2 end, float depth, IBrush fillBrush, IBrush strikeBrush,
float strikeSize, BorderKind kind = BorderKind.Center)
{
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
Vector2 min = Vector2.Min(start, end);
Vector2 max = Vector2.Max(start, end);
controller.EnsureBounds(new Box2d(min, max), depth);
controller.Write(DbBaseCommands.Instance.DrawRectFS, new RectCommandArgs(start, end, depth, fillBrush, strikeBrush, strikeSize, kind));
}
public static void Text(this DrawQueue queue, Vector3 position, IBrush brush, string text, IFont font,
Anchor anchor = Anchor.Left)
{
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
SizeF size = Typesetter.MeasureString(font, text);
controller.EnsureBounds(new Box2d(position.X, position.Y, position.X + size.Width, position.Y + size.Height), position.Z);
controller.Write(TextExtension.Instance.TextCommand, new TextCommandArgs(font, brush, anchor, position, text));
}
public static void Text(this DrawQueue queue, Vector3 position, IBrush textBrush, IBrush borderBrush,
float borderRadius, string text, IFont font, Anchor anchor = Anchor.Left, BorderKind borderKind = BorderKind.Outset)
{
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
SizeF size = Typesetter.MeasureString(font, text);
controller.EnsureBounds(new Box2d(position.X, position.Y, position.X + size.Width, position.Y + size.Height), position.Z);
controller.Write(TextExtension.Instance.TextCommand, new TextCommandArgs(font, textBrush, anchor, position, text)
{
BorderBrush = borderBrush,
BorderRadius = borderRadius,
BorderKind = borderKind,
});
}
}
}
-368
View File
@@ -1,368 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
namespace Dashboard.Drawing
{
public class DrawQueue : IEnumerable<ICommandFrame>, IDisposable
{
private readonly HashList<IDrawExtension> _extensions = new HashList<IDrawExtension>();
private readonly HashList<IDrawCommand> _commands = new HashList<IDrawCommand>();
private readonly HashList<IDrawResource> _resources = new HashList<IDrawResource>();
private readonly DrawController _controller;
private readonly MemoryStream _commandStream = new MemoryStream();
/// <summary>
/// The absolute boundary of all graphics objects.
/// </summary>
public Box3d Bounds { get; set; }
/// <summary>
/// The extensions required to draw the image.
/// </summary>
public IReadOnlyList<IDrawExtension> Extensions => _extensions;
/// <summary>
/// The resources used by this draw queue.
/// </summary>
public IReadOnlyList<IDrawResource> Resources => _resources;
/// <summary>
/// The list of commands used by the extension.
/// </summary>
public IReadOnlyList<IDrawCommand> Command => _commands;
public DrawQueue()
{
_controller = new DrawController(this);
}
/// <summary>
/// Clear the queue.
/// </summary>
public void Clear()
{
_resources.Clear();
_commands.Clear();
_extensions.Clear();
_commandStream.SetLength(0);
}
public int RequireExtension(IDrawExtension extension)
{
foreach (IDrawExtension super in extension.Requires)
RequireExtension(super);
return _extensions.Intern(extension);
}
public int RequireResource(IDrawResource resource)
{
RequireExtension(resource.Kind);
return _resources.Intern(resource);
}
internal IDrawController GetController(IDrawExtension extension)
{
_extensions.Intern(extension);
return _controller;
}
private void Write(IDrawCommand command)
{
if (command.Length > 0)
throw new InvalidOperationException("This command has a finite length argument.");
int cmdIndex = _commands.Intern(command);
Span<byte> cmd = stackalloc byte[6];
int sz;
if (command.Length == 0)
{
// Write a fixed command.
sz = ToVlq(cmdIndex, cmd);
}
else
{
// Write a variadic with zero length.
sz = ToVlq(cmdIndex, cmd);
cmd[sz++] = 0;
}
_commandStream.Write(cmd[..sz]);
}
private void Write(IDrawCommand command, ReadOnlySpan<byte> param)
{
if (command.Length < 0)
{
Span<byte> cmd = stackalloc byte[10];
int cmdIndex = _commands.Intern(command);
int sz = ToVlq(cmdIndex, cmd);
sz += ToVlq(param.Length, cmd[sz..]);
_commandStream.Write(cmd[..sz]);
_commandStream.Write(param);
}
else
{
if (command.Length != param.Length)
throw new ArgumentOutOfRangeException(nameof(param.Length), "Length of the parameter does not match the command.");
Span<byte> cmd = stackalloc byte[5];
int cmdIndex = _commands.Intern(command);
int sz = ToVlq(cmdIndex, cmd);
_commandStream.Write(cmd[..sz]);
_commandStream.Write(param);
}
}
public Enumerator GetEnumerator() => new Enumerator(this);
IEnumerator<ICommandFrame> IEnumerable<ICommandFrame>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
private static int ToVlq(int value, Span<byte> bytes)
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value), "Must be a positive integer.");
else if (bytes.Length < 5)
throw new ArgumentOutOfRangeException(nameof(bytes), "Must at least be five bytes long.");
if (value == 0)
{
bytes[0] = 0;
return 1;
}
int i;
for (i = 0; i < 5 && value != 0; i++, value >>= 7)
{
if (i > 0)
bytes[i - 1] |= 1 << 7;
bytes[i] = (byte)(value & 0x7F);
}
return i;
}
private static int FromVlq(ReadOnlySpan<byte> bytes, out int value)
{
value = 0;
int i;
for (i = 0; i < bytes.Length; i++)
{
byte b = bytes[i];
value |= (b & 0x7F) << (7*i);
if ((b & (1 << 7)) == 0)
{
i++;
break;
}
}
return i;
}
public void Dispose()
{
throw new NotImplementedException();
}
private class DrawController(DrawQueue Queue) : IDrawController
{
public void EnsureBounds(Box2d bounds, float depth)
{
Queue.Bounds = Box3d.Union(Queue.Bounds, bounds, depth);
}
public void Write(IDrawCommand command)
{
Queue.Write(command);
}
public void Write(IDrawCommand command, ReadOnlySpan<byte> bytes)
{
Queue.Write(command, bytes);
}
public void Write<T>(IDrawCommand command, T param) where T : IParameterSerializer<T>
{
int length = param.Serialize(Queue, Span<byte>.Empty);
Span<byte> bytes = stackalloc byte[length];
param.Serialize(Queue, bytes);
Write(command, bytes);
}
public void Write<T1, T2>(T2 command, T1 param) where T2 : IDrawCommand<T1>
{
int length = command.WriteParams(Queue, param, Span<byte>.Empty);
Span<byte> bytes = stackalloc byte[length];
command.WriteParams(Queue, param, bytes);
Write(command, bytes);
}
}
public class Enumerator : ICommandFrame, IEnumerator<ICommandFrame>
{
private readonly DrawQueue _queue;
private readonly byte[] _stream;
private int _length;
private int _index = -1;
private int _paramsIndex = -1;
private int _paramLength = 0;
private IDrawCommand? _current = null;
public ICommandFrame Current => this;
object? IEnumerator.Current => Current;
public IDrawCommand Command => _current ?? throw new InvalidOperationException();
public bool HasParameters { get; private set; }
public Enumerator(DrawQueue queue)
{
_queue = queue;
_stream = queue._commandStream.GetBuffer();
_length = (int)queue._commandStream.Length;
}
public bool MoveNext()
{
if (_index == -1)
_index = 0;
if (_index >= _length)
return false;
_index += FromVlq(_stream[_index .. (_index + 5)], out int command);
_current = _queue.Command[command];
HasParameters = _current.Length != 0;
if (!HasParameters)
{
_paramsIndex = -1;
return true;
}
int length;
if (_current.Length < 0)
{
_index += FromVlq(_stream[_index .. (_index + 5)], out length);
}
else
{
length = _current.Length;
}
_paramsIndex = _index;
_paramLength = length;
_index += length;
return true;
}
public void Reset()
{
_index = -1;
_current = null;
}
public object? GetParameter()
{
return _current?.GetParams(_queue, _stream.AsSpan(_paramsIndex, _paramLength));
}
public T GetParameter<T>()
{
if (_current is IDrawCommand<T> command)
{
return command.GetParams(_queue, _stream.AsSpan(_paramsIndex, _paramLength))!;
}
else
{
throw new InvalidOperationException();
}
}
public bool TryGetParameter<T>([NotNullWhen(true)] out T? parameter)
{
if (_current is IDrawCommand<T> command)
{
parameter = command.GetParams(_queue, _stream.AsSpan(_paramsIndex, _paramLength))!;
return true;
}
else
{
parameter = default;
return false;
}
}
public void Dispose()
{
}
}
}
public interface ICommandFrame
{
public IDrawCommand Command { get; }
public bool HasParameters { get; }
public object? GetParameter();
public T GetParameter<T>();
public bool TryGetParameter<T>([NotNullWhen(true)] out T? parameter);
}
public interface IDrawController
{
/// <summary>
/// Ensures that the canvas is at least a certain size.
/// </summary>
/// <param name="bounds">The bounding box.</param>
void EnsureBounds(Box2d bounds, float depth);
/// <summary>
/// Write into the command stream.
/// </summary>
/// <param name="command">The command to write.</param>
void Write(IDrawCommand command);
/// <summary>
/// Write into the command stream.
/// </summary>
/// <param name="command">The command to write.</param>
/// <param name="param">Any data associated with the command.</param>
void Write(IDrawCommand command, ReadOnlySpan<byte> param);
/// <summary>
/// Write into the command stream.
/// </summary>
/// <param name="command">The command to write.</param>
/// <param name="param">Any data associated with the command.</param>
void Write<T>(IDrawCommand command, T param) where T : IParameterSerializer<T>;
/// <summary>
/// Write into the command stream.
/// </summary>
/// <param name="command">The command to write.</param>
/// <param name="param">Any data associated with the command.</param>
void Write<T1, T2>(T2 command, T1 param) where T2 : IDrawCommand<T1>;
}
}
-9
View File
@@ -1,9 +0,0 @@
using Dashboard.Windowing;
namespace Dashboard.Drawing
{
public interface IDrawQueuePaintable : IPaintable
{
DrawQueue DrawQueue { get; }
}
}
-13
View File
@@ -1,13 +0,0 @@
namespace Dashboard.Drawing
{
/// <summary>
/// Interface for draw resources.
/// </summary>
public interface IDrawResource
{
/// <summary>
/// The extension for this kind of resource.
/// </summary>
IDrawExtension Kind { get; }
}
}
-14
View File
@@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Dashboard.Drawing
{
public interface IParameterSerializer<T>
{
int Serialize(DrawQueue queue, Span<byte> bytes);
void Deserialize(DrawQueue queue, ReadOnlySpan<byte> bytes);
}
}
-140
View File
@@ -1,140 +0,0 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Dashboard.Drawing
{
public class TextExtension : DrawExtension
{
public TextCommand TextCommand { get; }
private TextExtension() : base("DB_Text", new [] { BrushExtension.Instance })
{
TextCommand = new TextCommand(this);
}
public static readonly TextExtension Instance = new TextExtension();
}
public class TextCommand : IDrawCommand<TextCommandArgs>
{
public string Name { get; } = "Text";
public IDrawExtension Extension { get; }
public int Length { get; } = -1;
public TextCommand(TextExtension ext)
{
Extension = ext;
}
public int WriteParams(DrawQueue queue, TextCommandArgs obj, Span<byte> param)
{
int size = Unsafe.SizeOf<Header>() + obj.Text.Length * sizeof(char) + sizeof(char);
if (param.Length < size)
return size;
ref Header header = ref MemoryMarshal.Cast<byte, Header>(param[0..Unsafe.SizeOf<Header>()])[0];
Span<char> text = MemoryMarshal.Cast<byte, char>(param[Unsafe.SizeOf<Header>()..]);
header = new Header()
{
// Font = queue.RequireResource(obj.Font),
TextBrush = queue.RequireResource(obj.TextBrush),
BorderBrush = (obj.BorderBrush != null) ? queue.RequireResource(obj.BorderBrush) : -1,
BorderRadius = (obj.BorderBrush != null) ? obj.BorderRadius : 0f,
Anchor = obj.Anchor,
Position = obj.Position,
BorderKind = obj.BorderKind,
};
obj.Text.CopyTo(text);
return size;
}
public TextCommandArgs GetParams(DrawQueue queue, ReadOnlySpan<byte> param)
{
Header header = MemoryMarshal.Cast<byte, Header>(param[0..Unsafe.SizeOf<Header>()])[0];
ReadOnlySpan<char> text = MemoryMarshal.Cast<byte, char>(param[Unsafe.SizeOf<Header>()..]);
if (header.BorderBrush != -1 && header.BorderRadius != 0)
{
return new TextCommandArgs(
(IFont)queue.Resources[header.Font],
(IBrush)queue.Resources[header.TextBrush],
header.Anchor,
header.Position,
text.ToString())
{
BorderBrush = (IBrush)queue.Resources[header.BorderBrush],
BorderRadius = header.BorderRadius,
BorderKind = header.BorderKind,
};
}
else
{
return new TextCommandArgs(
(IFont)queue.Resources[header.Font],
(IBrush)queue.Resources[header.TextBrush],
header.Anchor,
header.Position,
text.ToString());
}
}
int IDrawCommand.WriteParams(DrawQueue queue, object? obj, Span<byte> param)
{
return WriteParams(queue, (TextCommandArgs)obj!, param);
}
object? IDrawCommand.GetParams(DrawQueue queue, ReadOnlySpan<byte> param)
{
return GetParams(queue, param);
}
private struct Header
{
private int _flags;
public int Font;
public int TextBrush;
public int BorderBrush;
public Vector3 Position;
public float BorderRadius;
public Anchor Anchor
{
get => (Anchor)(_flags & 0xF);
set => _flags = (_flags & ~0xF) | (int)value;
}
public BorderKind BorderKind
{
get => (_flags & INSET) switch
{
OUTSET => BorderKind.Outset,
INSET => BorderKind.Inset,
_ => BorderKind.Center,
};
set => _flags = value switch
{
BorderKind.Outset => (_flags & ~INSET) | OUTSET,
BorderKind.Inset => (_flags & ~INSET) | INSET,
_ => (_flags & ~INSET) | CENTER,
};
}
private const int INSET = 0x30;
private const int CENTER = 0x00;
private const int OUTSET = 0x10;
}
}
public record struct TextCommandArgs(IFont Font, IBrush TextBrush, Anchor Anchor, Vector3 Position, string Text)
{
public IBrush? BorderBrush { get; init; } = null;
public float BorderRadius { get; init; } = 0;
public BorderKind BorderKind { get; init; } = BorderKind.Center;
}
}
-105
View File
@@ -1,105 +0,0 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.IO;
using System.Reflection.PortableExecutable;
namespace Dashboard.Drawing
{
/// <summary>
/// Interface for registered typesetters.
/// </summary>
public interface ITypeSetter
{
/// <summary>
/// Name of the typesetter.
/// </summary>
string Name { get; }
SizeF MeasureString(IFont font, string value);
IFont LoadFont(Stream stream);
IFont LoadFont(string path);
// IFont LoadFont(NamedFont font);
}
/// <summary>
/// Class for typesetting related functions.
/// </summary>
public static class Typesetter
{
/// <summary>
/// The typesetting backend for this instance.
/// </summary>
public static ITypeSetter Backend { get; set; } = new UndefinedTypeSetter();
public static string Name => Backend.Name;
public static SizeF MeasureString(IFont font, string value)
{
return Backend.MeasureString(font, value);
}
public static IFont LoadFont(Stream stream)
{
return Backend.LoadFont(stream);
}
public static IFont LoadFont(string path)
{
return Backend.LoadFont(path);
}
public static IFont LoadFont(FileInfo file)
{
return Backend.LoadFont(file.FullName);
}
// public static IFont LoadFont(NamedFont font)
// {
// return Backend.LoadFont(font);
// }
public static IFont LoadFont(string family, float size, FontWeight weight = FontWeight.Normal,
FontSlant slant = FontSlant.Normal, FontStretch stretch = FontStretch.Normal)
{
// return LoadFont(new NamedFont(family, size, weight, slant, stretch));
throw new Exception();
}
private class UndefinedTypeSetter : ITypeSetter
{
public string Name { get; } = "Undefined";
[DoesNotReturn]
private void Except()
{
throw new InvalidOperationException("No typesetting backend is loaded.");
}
public SizeF MeasureString(IFont font, string value)
{
Except();
return default;
}
public IFont LoadFont(Stream stream)
{
Except();
return default;
}
public IFont LoadFont(string path)
{
Except();
return default;
}
// public IFont LoadFont(NamedFont font)
// {
// Except();
// return default;
// }
}
}
}
@@ -1,9 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
-234
View File
@@ -1,234 +0,0 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Numerics;
using System.Text;
using Dashboard.Drawing;
namespace Dashboard.ImmediateUI
{
public class DimUIConfig
{
public Vector2 Margin = new Vector2(8, 4);
public Vector2 Padding = new Vector2(4);
public required IFont Font { get; init; }
public IBrush TextBrush = new SolidBrush(Color.Black);
public IBrush DisabledText = new SolidBrush(Color.Gray);
public float ButtonBorderSize = 2f;
public IBrush ButtonBorderBrush = new SolidBrush(Color.SteelBlue);
public IBrush ButtonFillBrush = new SolidBrush(Color.SlateGray);
public IBrush? ButtonShadowBrush = new SolidBrush(Color.FromArgb(32, Color.LightSteelBlue));
public float ButtonShadowOffset = 2f;
public float InputBorderSize = 2f;
public IBrush InputPlaceholderTextBrush = new SolidBrush(Color.SteelBlue);
public IBrush InputBorderBrush = new SolidBrush(Color.SlateGray);
public IBrush InputFillBrush = new SolidBrush(Color.LightGray);
public IBrush? InputShadowBrush = new SolidBrush(Color.FromArgb(32, Color.LightSteelBlue));
public float InputShadowOffset = -2f;
public float MenuBorderSize = 2f;
public IBrush MenuBorderBrush = new SolidBrush(Color.SteelBlue);
public IBrush MenuFillBrush = new SolidBrush(Color.SlateGray);
public IBrush? MenuShadowBrush = new SolidBrush(Color.FromArgb(32, Color.LightSteelBlue));
public float MenuShadowOffset = 2f;
}
public class DimUI
{
private readonly DimUIConfig _config;
private Vector2 _pen;
private Box2d _bounds;
private bool _firstLine = false;
private bool _sameLine = false;
private float _z = -1;
private float _lineHeight;
private DrawQueue _queue;
public DimUI(DimUIConfig config)
{
_config = config;
}
[MemberNotNull(nameof(_queue))]
public void Begin(Box2d bounds, DrawQueue queue)
{
_bounds = bounds;
_pen = _bounds.Min;
_queue = queue;
_firstLine = true;
_lineHeight = 0;
_z = -1;
}
public void SameLine()
{
_sameLine = true;
}
private void Line()
{
if (!_firstLine && !_sameLine)
{
_pen = new Vector2(_bounds.Left, _pen.Y + _lineHeight);
}
else
{
_firstLine = false;
_sameLine = false;
}
_pen.X += _config.Margin.X;
_lineHeight = 0;
}
private float Z()
{
return _z += 0.001f;
}
public void Text(string text)
{
Line();
SizeF sz = Typesetter.MeasureString(_config.Font, text);
float z = Z();
float h = _config.Margin.Y * 2 + sz.Height;
_queue.Text(new Vector3(_pen + new Vector2(0, _config.Margin.X), z), _config.TextBrush, text, _config.Font);
_lineHeight = Math.Max(_lineHeight, h);
_pen.X += sz.Width;
}
public void DrawBox(
Vector2 position,
Vector2 size,
IBrush fill,
IBrush border, float borderWidth,
IBrush? shadow, float offset)
{
float z = Z();
if (shadow != null)
{
if (offset >= 0)
{
_queue.Rect(position + new Vector2(offset), position + size + new Vector2(offset + borderWidth), z, shadow);
}
else
{
// Inset shadows are draw a bit weirdly.
_queue.Rect(position, position + new Vector2(offset, size.Y), z, shadow);
_queue.Rect(position + new Vector2(offset, 0), position + new Vector2(size.X - offset, offset), z, shadow);
}
}
_queue.Rect(position, position + size, z, fill, border, borderWidth, BorderKind.Outset);
}
public bool Button(string label)
{
Line();
SizeF sz = Typesetter.MeasureString(_config.Font, label);
float h = _config.Margin.Y * 2 + _config.Padding.Y * 2 + sz.Height;
DrawBox(
_pen + new Vector2(0, _config.Margin.Y),
new Vector2(sz.Width + 2 * _config.Padding.X, sz.Height + 2 * _config.Padding.Y),
_config.ButtonFillBrush,
_config.ButtonBorderBrush,
_config.ButtonBorderSize,
_config.ButtonShadowBrush,
_config.ButtonShadowOffset);
float z = Z();
_queue.Text(new Vector3(_pen + new Vector2(_config.Padding.X, _config.Margin.Y + _config.Padding.Y), z),
_config.TextBrush, label, _config.Font);
_lineHeight = Math.Max(_lineHeight, h);
_pen.X += sz.Width + 2 * _config.Padding.X;
return false;
}
public bool Input(string placeholder, StringBuilder value)
{
Line();
IBrush textBrush;
string str;
if (value.Length == 0)
{
textBrush = _config.DisabledText;
str = placeholder;
}
else
{
textBrush = _config.TextBrush;
str = value.ToString();
}
SizeF sz = Typesetter.MeasureString(_config.Font, str);
float h = _config.Margin.Y * 2 + _config.Padding.Y * 2 + sz.Height;
DrawBox(
_pen + new Vector2(0, _config.Margin.Y),
new Vector2(sz.Width + 2 * _config.Padding.X, sz.Height + 2 * _config.Padding.Y),
_config.InputFillBrush,
_config.InputBorderBrush,
_config.InputBorderSize,
_config.InputShadowBrush,
_config.InputShadowOffset);
float z = Z();
_queue.Text(new Vector3(_pen + new Vector2(_config.Padding.X, _config.Margin.Y + _config.Padding.Y), z),
textBrush, str, _config.Font);
_lineHeight = Math.Max(_lineHeight, h);
_pen.X += sz.Width + 2 * _config.Padding.X;
return false;
}
public void BeginMenu()
{
}
public bool MenuItem(string name)
{
return false;
}
public void EndMenu()
{
}
public int Id(ReadOnlySpan<char> str)
{
// Uses the FVN-1A algorithm in 32-bit mode.
const int PRIME = 0x01000193;
const int BASIS = unchecked((int)0x811c9dc5);
int hash = BASIS;
for (int i = 0; i < str.Length; i++)
{
hash ^= str[i] & 0xFF;
hash *= PRIME;
hash ^= str[i] >> 8;
hash *= PRIME;
}
return hash;
}
public void Finish()
{
// TODO:
}
}
}
+41 -6
View File
@@ -6,29 +6,45 @@ using OpenTK.Graphics.OpenGL;
namespace Dashboard.OpenGL.Drawing namespace Dashboard.OpenGL.Drawing
{ {
public class DirectRendering : IDirectRendering public class DirectRendering : IDirectRendering, ITextureExtension
{ {
public DeviceContext Context { get; private set; } = null!; public GLDeviceContext Context { get; private set; } = null!;
public string DriverName { get; } = "Dashboard OpenGL"; public string DriverName { get; } = "Dashboard OpenGL";
public string DriverVendor { get; } = "Dashboard"; public string DriverVendor { get; } = "Dashboard";
public Version DriverVersion { get; } = new Version(0, 1); public Version DriverVersion { get; } = new Version(0, 1);
IContextBase IContextExtensionBase.Context => Context; IContextBase IContextExtensionBase.Context => Context;
DeviceContext IContextExtensionBase<DeviceContext>.Context => Context;
public bool SupportsArbTextureStorage { get; private set; }
public bool SupportsAnisotropy { get; private set; }
private int _vao = -1; private int _vao = -1;
private List<GLTexture> _textures = new List<GLTexture>();
public void Dispose() public void Dispose()
{ {
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
public void Require(DeviceContext context) => Context = context; public void Require(DeviceContext context)
{
Context = (GLDeviceContext)context;
SupportsArbTextureStorage = Context.DriverVersion >= new Version(4, 2) ||
Context.IsGLExtensionAvailable("GL_ARB_texture_storage");
SupportsAnisotropy = Context.DriverVersion >= new Version() ||
Context.IsGLExtensionAvailable("GL_EXT_texture_filter_anisotropic") ||
Context.IsGLExtensionAvailable("GL_ARB_texture_filter_anisotropic");
}
public void Require(IContextBase context) => Require((DeviceContext)context); public void Require(IContextBase context) => Require((DeviceContext)context);
public void Begin() public void Begin()
{ {
if (_vao == -1) if (_vao == -1)
{ {
GL.CreateVertexArray(out _vao); GL.GenVertexArray(out _vao);
} }
} }
@@ -37,7 +53,7 @@ namespace Dashboard.OpenGL.Drawing
} }
public IShader CreatePipeline<T>(T createInfo) where T : IShaderCreateInfo public IShader CreateShader<T>(T createInfo) where T : IShaderCreateInfo
{ {
int program = createInfo switch int program = createInfo switch
{ {
@@ -60,7 +76,7 @@ namespace Dashboard.OpenGL.Drawing
Vector2 size = Context.FramebufferSize; Vector2 size = Context.FramebufferSize;
drawCall.SetAll(size, _vao, false); drawCall.SetAll(size, _vao, false);
if (drawCall.ElementBuffer is null) if (drawCall.VertexSpecification.ElementBuffer is null)
{ {
GL.DrawArrays(drawCall.Primitive.OpenGL, drawCall.First, drawCall.Count); GL.DrawArrays(drawCall.Primitive.OpenGL, drawCall.First, drawCall.Count);
} }
@@ -74,5 +90,24 @@ namespace Dashboard.OpenGL.Drawing
} }
} }
public GLTexture CreateTexture(TextureType type)
{
GLTexture texture = new GLTexture(this, type);
lock (_textures) _textures.Add(texture);
return texture;
}
internal void TextureDisposed(GLTexture texture)
{
lock (_textures) _textures.Remove(texture);
}
ITexture ITextureExtension.CreateTexture(TextureType type) => CreateTexture(type);
ITexture IDirectRendering.CreateTexture(TextureType type)
{
return CreateTexture(type);
}
} }
} }
+125 -3
View File
@@ -1,11 +1,133 @@
using System.Collections.Immutable;
using System.Security.Cryptography.X509Certificates;
using Dashboard.Drawing; using Dashboard.Drawing;
using OpenTK.Graphics.OpenGL;
namespace Dashboard.OpenGL.Drawing namespace Dashboard.OpenGL.Drawing
{ {
public class GLShader(GLDeviceContext context, int handle) : IShader public class GLShader : IShader
{ {
public GLDeviceContext Context { get; } = context; public GLDeviceContext Context { get; }
public int Handle { get; } = handle; public int Handle { get; }
public ImmutableList<ShaderMappingProperty> Attributes { get; }
public ImmutableList<ShaderMappingProperty> Uniforms { get; }
public ImmutableList<ShaderMappingProperty> Blocks { get; }
public ImmutableList<ShaderMappingProperty> Textures { get; }
public GLShader(GLDeviceContext context, int handle)
{
Context = context;
Handle = handle;
List<ShaderMappingProperty> list = new List<ShaderMappingProperty>();
List<ShaderMappingProperty> list2 = new List<ShaderMappingProperty>();
int count = GL.GetProgrami(handle, ProgramProperty.ActiveAttributes);
for (int i = 0; i < count; i++)
{
list.Add(GetAttribute(handle, i));
}
Attributes = list.ToImmutableList();
list.Clear();
count = GL.GetProgrami(handle, ProgramProperty.ActiveUniforms);
for (int i = 0; i < count; i++)
{
var property = GetUniform(handle, i, out bool isSampler);
list.Add(property);
if (isSampler)
list2.Add(property);
}
Uniforms = list.ToImmutableList();
Textures = list2.ToImmutableList();
list.Clear();
count = GL.GetProgrami(handle, ProgramProperty.ActiveUniformBlocks);
for (int i = 0; i < count; i++)
{
var property = GetBlock(handle, i);
list.Add(property);
}
Blocks = list.ToImmutableList();
}
private static ShaderMappingProperty GetAttribute(int program, int index)
{
string name = GL.GetActiveAttrib(program, (uint)index, 260, out _, out _, out _);
int location = GL.GetAttribLocation(program, name);
return new ShaderMappingProperty(name, location);
}
private static ShaderMappingProperty GetUniform(int program, int index, out bool isSampler)
{
string name = GL.GetActiveUniform(program, (uint)index, 260, out _, out _, out OpenTK.Graphics.OpenGL.UniformType type);
int location = GL.GetUniformLocation(program, name);
isSampler = type switch
{
OpenTK.Graphics.OpenGL.UniformType.Sampler1D or
OpenTK.Graphics.OpenGL.UniformType.Sampler1DArray or
OpenTK.Graphics.OpenGL.UniformType.Sampler1DShadow or
OpenTK.Graphics.OpenGL.UniformType.Sampler1DArrayShadow or
OpenTK.Graphics.OpenGL.UniformType.IntSampler1D or
OpenTK.Graphics.OpenGL.UniformType.IntSampler1DArray or
OpenTK.Graphics.OpenGL.UniformType.UnsignedIntSampler1D or
OpenTK.Graphics.OpenGL.UniformType.UnsignedIntSampler1DArray or
OpenTK.Graphics.OpenGL.UniformType.Sampler2D or
OpenTK.Graphics.OpenGL.UniformType.Sampler2DShadow or
OpenTK.Graphics.OpenGL.UniformType.Sampler2DRect or
OpenTK.Graphics.OpenGL.UniformType.Sampler2DRectShadow or
OpenTK.Graphics.OpenGL.UniformType.Sampler2DArray or
OpenTK.Graphics.OpenGL.UniformType.Sampler2DArrayShadow or
OpenTK.Graphics.OpenGL.UniformType.Sampler2DMultisample or
OpenTK.Graphics.OpenGL.UniformType.Sampler2DMultisampleArray or
OpenTK.Graphics.OpenGL.UniformType.IntSampler2D or
OpenTK.Graphics.OpenGL.UniformType.IntSampler2DRect or
OpenTK.Graphics.OpenGL.UniformType.IntSampler2DArray or
OpenTK.Graphics.OpenGL.UniformType.IntSampler2DMultisample or
OpenTK.Graphics.OpenGL.UniformType.IntSampler2DMultisampleArray or
OpenTK.Graphics.OpenGL.UniformType.UnsignedIntSampler2D or
OpenTK.Graphics.OpenGL.UniformType.UnsignedIntSampler2DRect or
OpenTK.Graphics.OpenGL.UniformType.UnsignedIntSampler2DArray or
OpenTK.Graphics.OpenGL.UniformType.UnsignedIntSampler2DMultisample or
OpenTK.Graphics.OpenGL.UniformType.UnsignedIntSampler2DMultisampleArray or
OpenTK.Graphics.OpenGL.UniformType.Sampler3D or
OpenTK.Graphics.OpenGL.UniformType.IntSampler3D or
OpenTK.Graphics.OpenGL.UniformType.UnsignedIntSampler3D or
OpenTK.Graphics.OpenGL.UniformType.SamplerCube or
OpenTK.Graphics.OpenGL.UniformType.SamplerCubeShadow or
OpenTK.Graphics.OpenGL.UniformType.SamplerCubeMapArray or
OpenTK.Graphics.OpenGL.UniformType.SamplerCubeMapArrayShadow or
OpenTK.Graphics.OpenGL.UniformType.IntSamplerCube or
OpenTK.Graphics.OpenGL.UniformType.IntSamplerCubeMapArray or
OpenTK.Graphics.OpenGL.UniformType.UnsignedIntSamplerCube or
OpenTK.Graphics.OpenGL.UniformType.UnsignedIntSamplerCubeMapArray
=> true,
_ => false,
};
return new ShaderMappingProperty(name, location);
}
private static ShaderMappingProperty GetBlock(int program, int index)
{
int size = 0;
unsafe
{
GL.GetActiveUniformBlockiv(program, (uint)index, UniformBlockPName.UniformBlockDataSize, &size);
}
GL.GetActiveUniformBlockName(program, (uint)index, 1024, out _, out string name);
return new ShaderMappingProperty(name, index);
}
public void Dispose() public void Dispose()
{ {
+63 -38
View File
@@ -221,16 +221,12 @@ namespace Dashboard.OpenGL.Drawing
} }
} }
extension(DrawCall call) extension(PipelineState state)
{ {
public void UsePipeline() => GL.UseProgram((call.ShaderPipeline as GLShader)?.Handle ?? 0);
// TODO: clamp to viewport size
public void SetViewport(Vector2 size) public void SetViewport(Vector2 size)
{ {
Vector2 min = Vector2.Max(Vector2.Zero, call.ViewportRegion.Min); Vector2 min = Vector2.Max(Vector2.Zero, state.ViewportRegion.Min);
Vector2 max = Vector2.Min(size, call.ViewportRegion.Max); Vector2 max = Vector2.Min(size, state.ViewportRegion.Max);
GL.Viewport( GL.Viewport(
(int)Math.Round(min.X), (int)Math.Round(min.X),
@@ -239,9 +235,10 @@ namespace Dashboard.OpenGL.Drawing
(int)Math.Round(max.Y - min.Y)); (int)Math.Round(max.Y - min.Y));
} }
// TODO: clamp to viewport size
public void SetScissor(Vector2 size) public void SetScissor(Vector2 size)
{ {
if (call.ScissorRegion == new Box2d(Vector2.PositiveInfinity, Vector2.PositiveInfinity)) if (state.ScissorRegion == new Box2d(Vector2.PositiveInfinity, Vector2.PositiveInfinity))
{ {
GL.Disable(EnableCap.ScissorTest); GL.Disable(EnableCap.ScissorTest);
return; return;
@@ -249,15 +246,15 @@ namespace Dashboard.OpenGL.Drawing
GL.Enable(EnableCap.ScissorTest); GL.Enable(EnableCap.ScissorTest);
GL.Scissor( GL.Scissor(
(int)Math.Round(call.ViewportRegion.Left), (int)Math.Round(state.ViewportRegion.Left),
(int)Math.Round(size.Y - call.ViewportRegion.Top), (int)Math.Round(size.Y - state.ViewportRegion.Top),
(int)Math.Round(call.ViewportRegion.Right - call.ViewportRegion.Left), (int)Math.Round(state.ViewportRegion.Right - state.ViewportRegion.Left),
(int)Math.Round(call.ViewportRegion.Bottom - call.ViewportRegion.Top)); (int)Math.Round(state.ViewportRegion.Bottom - state.ViewportRegion.Top));
} }
public void SetFrontFace() public void SetFrontFace()
{ {
GL.FrontFace(call.FrontFace switch GL.FrontFace(state.FrontFace switch
{ {
WindingOrder.Clockwise => FrontFaceDirection.Cw, WindingOrder.Clockwise => FrontFaceDirection.Cw,
WindingOrder.Counterclockwise => FrontFaceDirection.Ccw, WindingOrder.Counterclockwise => FrontFaceDirection.Ccw,
@@ -267,7 +264,7 @@ namespace Dashboard.OpenGL.Drawing
public void SetCullMode() public void SetCullMode()
{ {
if (call.CullMode == FaceCulling.None) if (state.CullMode == FaceCulling.None)
{ {
GL.Disable(EnableCap.CullFace); GL.Disable(EnableCap.CullFace);
return; return;
@@ -275,7 +272,7 @@ namespace Dashboard.OpenGL.Drawing
GL.Enable(EnableCap.CullFace); GL.Enable(EnableCap.CullFace);
GL.CullFace(call.CullMode switch GL.CullFace(state.CullMode switch
{ {
FaceCulling.Both => TriangleFace.FrontAndBack, FaceCulling.Both => TriangleFace.FrontAndBack,
FaceCulling.Front => TriangleFace.Front, FaceCulling.Front => TriangleFace.Front,
@@ -284,9 +281,47 @@ namespace Dashboard.OpenGL.Drawing
}); });
} }
public void SetBlendMode() => call.BlendMode.SetAll(); public void SetBlendMode() => state.BlendMode.SetAll();
public void SetDepthMode() => call.DepthMode.SetAll(); public void SetDepthMode() => state.DepthMode.SetAll();
public void SetStencilMode() => call.StencilMode.SetAll(); public void SetStencilMode() => state.StencilMode.SetAll();
public void SetColorMask()
{
GL.ColorMask(state.RedMask, state.GreenMask, state.BlueMask, state.AlphaMask);
}
public void SetPointSize()
{
GL.PointSize(state.PointSize);
}
public void SetLineWidth()
{
GL.LineWidth(state.LineWidth);
}
}
extension(DrawCall call)
{
public void UsePipeline() => GL.UseProgram((call.ShaderPipeline as GLShader)?.Handle ?? 0);
public void SetViewport(Vector2 size) => call.PipelineState.SetViewport(size);
public void SetScissor(Vector2 size) => call.PipelineState.SetViewport(size);
public void SetFrontFace() => call.PipelineState.SetFrontFace();
public void SetCullMode() => call.PipelineState.SetCullMode();
public void SetBlendMode() => call.PipelineState.BlendMode.SetAll();
public void SetDepthMode() => call.PipelineState.DepthMode.SetAll();
public void SetStencilMode() => call.PipelineState.StencilMode.SetAll();
public void SetColorMask() => call.PipelineState.SetColorMask();
public void SetPointSize() => call.PipelineState.SetPointSize();
public void SetLineWidth() => call.PipelineState.SetLineWidth();
public void SetPrimitiveRestart() public void SetPrimitiveRestart()
{ {
@@ -300,21 +335,6 @@ namespace Dashboard.OpenGL.Drawing
GL.PrimitiveRestartIndex((uint)call.RestartIndex); GL.PrimitiveRestartIndex((uint)call.RestartIndex);
} }
public void SetColorMask()
{
GL.ColorMask(call.RedMask, call.GreenMask, call.BlueMask, call.AlphaMask);
}
public void SetPointSize()
{
GL.PointSize(call.PointSize);
}
public void SetLineWidth()
{
GL.LineWidth(call.LineWidth);
}
public void UseTextures() public void UseTextures()
{ {
int i = 0; int i = 0;
@@ -336,15 +356,21 @@ namespace Dashboard.OpenGL.Drawing
{ {
GL.BindVertexArray(vao); GL.BindVertexArray(vao);
if (call.ElementBuffer != null) VertexSpecification spec = call.VertexSpecification;
if (spec.ElementBuffer != null)
{ {
GL.BindBuffer(BufferTarget.ElementArrayBuffer, (call.ElementBuffer as GLBuffer)?.Handle ?? 0); GL.BindBuffer(BufferTarget.ElementArrayBuffer, (spec.ElementBuffer as GLBuffer)?.Handle ?? 0);
} }
GL.BindBuffer(BufferTarget.ArrayBuffer, (call.VertexBuffer as GLBuffer)?.Handle ?? 0);
foreach(VertexAttribute attrib in call.Attributes) for (int i = 0; i < spec.Attributes.Count; i++)
{ {
VertexAttribute attrib = spec.Attributes[i];
IBuffer? buffer = spec.VertexBuffers[i];
GL.BindBuffer(BufferTarget.ArrayBuffer, (buffer as GLBuffer)?.Handle ?? 0);
GL.VertexAttribPointer( GL.VertexAttribPointer(
(uint)attrib.Location, (uint)attrib.Location,
attrib.Components, attrib.Components,
@@ -462,7 +488,6 @@ namespace Dashboard.OpenGL.Drawing
call.SetStencilMode(); call.SetStencilMode();
call.SetPrimitiveRestart(); call.SetPrimitiveRestart();
call.SetColorMask(); call.SetColorMask();
call.SetDepthMode();
call.SetPointSize(); call.SetPointSize();
call.SetLineWidth(); call.SetLineWidth();
call.UseTextures(); call.UseTextures();
@@ -1,61 +1,12 @@
using System.Drawing; using System.Drawing;
using Dashboard.Drawing; using Dashboard.Drawing;
using Dashboard.Pal; using Dashboard.OpenGL.Drawing;
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using OGL = OpenTK.Graphics.OpenGL; using OGL = OpenTK.Graphics.OpenGL;
namespace Dashboard.OpenGL namespace Dashboard.OpenGL
{ {
public class GLTextureExtension : ITextureExtension, IContextExtensionBase<GLDeviceContext> public class GLTexture(DirectRendering extension, TextureType type) : ITexture
{
public string DriverName => "Dashboard OpenGL Texture Extension";
public string DriverVendor => "Dashboard";
public Version DriverVersion => new Version(0, 1, 0);
public GLDeviceContext Context { get; private set; } = null!;
public bool SupportsArbTextureStorage { get; private set; }
public bool SupportsAnisotropy { get; private set; }
IContextBase IContextExtensionBase.Context => Context;
DeviceContext IContextExtensionBase<DeviceContext>.Context => Context;
private List<GLTexture> _textures = new List<GLTexture>();
public void Dispose()
{
}
public void Require(GLDeviceContext context)
{
Context = context;
SupportsArbTextureStorage = Context.DriverVersion >= new Version(4, 2) ||
Context.IsGLExtensionAvailable("GL_ARB_texture_storage");
SupportsAnisotropy = Context.DriverVersion >= new Version() ||
Context.IsGLExtensionAvailable("GL_EXT_texture_filter_anisotropic") ||
Context.IsGLExtensionAvailable("GL_ARB_texture_filter_anisotropic");
}
public void Require(DeviceContext context) => Require((GLDeviceContext)context);
public void Require(IContextBase context) => Require((GLDeviceContext)context);
public GLTexture CreateTexture(TextureType type)
{
GLTexture texture = new GLTexture(this, type);
lock (_textures) _textures.Add(texture);
return texture;
}
internal void TextureDisposed(GLTexture texture)
{
lock (_textures) _textures.Remove(texture);
}
ITexture ITextureExtension.CreateTexture(TextureType type) => CreateTexture(type);
}
public class GLTexture(GLTextureExtension extension, TextureType type) : ITexture
{ {
public int Handle { get; private set; } = 0; public int Handle { get; private set; } = 0;
public bool IsValid => Handle != 0; public bool IsValid => Handle != 0;
@@ -87,7 +38,7 @@ namespace Dashboard.OpenGL
_ => throw new NotSupportedException() _ => throw new NotSupportedException()
}; };
private GLTextureExtension Extension { get; } = extension; private DirectRendering Extension { get; } = extension;
private GLDeviceContext Context => Extension.Context; private GLDeviceContext Context => Extension.Context;
~GLTexture() ~GLTexture()
+89 -115
View File
@@ -1,6 +1,6 @@
using System.Drawing; using System.Drawing;
using System.Numerics; using System.Numerics;
using System.Runtime; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Dashboard.Drawing; using Dashboard.Drawing;
using Dashboard.Pal; using Dashboard.Pal;
@@ -16,14 +16,15 @@ namespace Dashboard.OpenGL.Drawing
public DeviceContext Context { get; private set; } = null!; public DeviceContext Context { get; private set; } = null!;
private int _program; private IDirectRendering _dr;
private GLShader _program;
private uint _program_apos; private uint _program_apos;
private uint _program_atexcoord; private uint _program_atexcoord;
private uint _program_acolor; private uint _program_acolor;
private int _program_transforms; private int _program_transforms;
private int _program_image; private int _program_image;
private int _vao; private GLTexture _white;
private int _white;
public void Dispose() public void Dispose()
{ {
@@ -32,51 +33,46 @@ namespace Dashboard.OpenGL.Drawing
public void Require(DeviceContext context) public void Require(DeviceContext context)
{ {
Context = context; Context = context;
_dr = context.ExtensionRequire<IDirectRendering>();
_program = GL.CreateProgram(); GlslShaderCreateInfo shader;
int vs = GL.CreateShader(ShaderType.VertexShader); using (StreamReader vertex = new StreamReader(GetType().Assembly
using (StreamReader reader = new StreamReader(GetType().Assembly
.GetManifestResourceStream("Dashboard.OpenGL.Drawing.immediate.vert")!)) .GetManifestResourceStream("Dashboard.OpenGL.Drawing.immediate.vert")!))
{ using (StreamReader fragment = new StreamReader(GetType().Assembly
GL.ShaderSource(vs, reader.ReadToEnd());
}
GL.CompileShader(vs);
GL.AttachShader(_program, vs);
int fs = GL.CreateShader(ShaderType.FragmentShader);
using (StreamReader reader = new StreamReader(GetType().Assembly
.GetManifestResourceStream("Dashboard.OpenGL.Drawing.immediate.frag")!)) .GetManifestResourceStream("Dashboard.OpenGL.Drawing.immediate.frag")!))
{ {
GL.ShaderSource(fs, reader.ReadToEnd()); shader = new GlslShaderCreateInfo()
{
VertexShader = vertex.ReadToEnd(),
FragmentShader = fragment.ReadToEnd(),
};
} }
GL.CompileShader(fs); _program = (GLShader)_dr.CreateShader(shader);
GL.AttachShader(_program, fs);
GL.LinkProgram(_program); _program_apos = (uint)GL.GetAttribLocation(_program.Handle, "aPos");
GL.DeleteShader(vs); GL.DeleteShader(fs); _program_atexcoord = (uint)GL.GetAttribLocation(_program.Handle, "aTexCoords");
_program_acolor = (uint)GL.GetAttribLocation(_program.Handle, "aColor");
_program_apos = (uint)GL.GetAttribLocation(_program, "aPos"); _program_transforms = GL.GetUniformLocation(_program.Handle, "transforms");
_program_atexcoord = (uint)GL.GetAttribLocation(_program, "aTexCoords"); _program_image = GL.GetUniformLocation(_program.Handle, "image");
_program_acolor = (uint)GL.GetAttribLocation(_program, "aColor");
_program_transforms = GL.GetUniformLocation(_program, "transforms"); _white = (GLTexture)_dr.CreateTexture(TextureType.Texture2D);
_program_image = GL.GetUniformLocation(_program, "image"); _white.SetStorage(PixelFormat.Rgb8I, 1, 1, 1, 1);
_white.Swizzle = new ColorSwizzle(ColorChannel.One, ColorChannel.One, ColorChannel.One, ColorChannel.One);
_white.MinifyFilter = TextureFilter.Nearest;
_white.MagnifyFilter = TextureFilter.Nearest;
GL.GenTexture(out _white); // TODO: implement the API above instead of writing this manually.
GL.BindTexture(TextureTarget.Texture2D, _white); GL.BindTexture(TextureTarget.Texture2D, _white.Handle);
GL.TexImage2D(TextureTarget.Texture2D, 0, InternalFormat.Rgb, 1, 1, 0, OpenTK.Graphics.OpenGL.PixelFormat.Rgb, PixelType.Byte, IntPtr.Zero); GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, 1, 1, OpenTK.Graphics.OpenGL.PixelFormat.Rgb, PixelType.Byte, stackalloc byte[] { 0xFF, 0xFF, 0xFF, 0xFF });
GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleA, (int)All.One); GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleA, (int)All.One);
GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleR, (int)All.One); GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleR, (int)All.One);
GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleG, (int)All.One); GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleG, (int)All.One);
GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleB, (int)All.One); GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleB, (int)All.One);
GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest); GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest); GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
GL.GenVertexArray(out _vao);
} }
public void ClearColor(Color color) public void ClearColor(Color color)
@@ -99,31 +95,11 @@ namespace Dashboard.OpenGL.Drawing
new ImmediateVertex(new Vector3(a+tangent, depth), Vector2.Zero, color), new ImmediateVertex(new Vector3(a+tangent, depth), Vector2.Zero, color),
]; ];
int buffer = GL.GenBuffer(); DrawImmediate(new ImmediateDrawCall(MeshPrimitive.Triangle, vertices.ToArray())
GL.BindBuffer(BufferTarget.ArrayBuffer, buffer); {
GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * ImmediateVertex.Size, ref vertices[0], BufferUsage.StreamDraw); Transforms = Context.ExtensionRequire<IDeviceContextBase>().Transforms,
PipelineState = new PipelineState { CullMode = FaceCulling.None },
GL.BindVertexArray(_vao); });
GL.VertexAttribPointer(_program_apos, 3, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.PosOffset);
GL.EnableVertexAttribArray(_program_apos);
GL.VertexAttribPointer(_program_atexcoord, 2, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.TexCoordsOffset);
GL.EnableVertexAttribArray(_program_atexcoord);
GL.VertexAttribPointer(_program_acolor, 4, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.ColorOffset);
GL.EnableVertexAttribArray(_program_acolor);
Matrix4x4 view = Context.ExtensionRequire<IDeviceContextBase>().Transforms;
GL.UseProgram(_program);
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, _white);
GL.UniformMatrix4f(_program_transforms, 1, true, ref view);
GL.Uniform1i(_program_image, 0);
GL.DrawArrays(PrimitiveType.Triangles, 0, 6);
GL.DeleteBuffer(buffer);
} }
public void Rectangle(Box2d rectangle, float depth, Vector4 color) public void Rectangle(Box2d rectangle, float depth, Vector4 color)
@@ -138,31 +114,11 @@ namespace Dashboard.OpenGL.Drawing
new ImmediateVertex(new Vector3(rectangle.Min.X, rectangle.Max.Y, depth), Vector2.Zero, color), new ImmediateVertex(new Vector3(rectangle.Min.X, rectangle.Max.Y, depth), Vector2.Zero, color),
]; ];
int buffer = GL.GenBuffer(); DrawImmediate(new ImmediateDrawCall(MeshPrimitive.Triangle, vertices.ToArray())
GL.BindBuffer(BufferTarget.ArrayBuffer, buffer); {
GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * ImmediateVertex.Size, ref vertices[0], BufferUsage.StreamDraw); Transforms = Context.ExtensionRequire<IDeviceContextBase>().Transforms,
PipelineState = new PipelineState { CullMode = FaceCulling.None },
GL.BindVertexArray(_vao); });
GL.VertexAttribPointer(_program_apos, 3, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.PosOffset);
GL.EnableVertexAttribArray(_program_apos);
GL.VertexAttribPointer(_program_atexcoord, 2, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.TexCoordsOffset);
GL.EnableVertexAttribArray(_program_atexcoord);
GL.VertexAttribPointer(_program_acolor, 4, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.ColorOffset);
GL.EnableVertexAttribArray(_program_acolor);
Matrix4x4 view = Context.ExtensionRequire<IDeviceContextBase>().Transforms;
GL.UseProgram(_program);
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, _white);
GL.UniformMatrix4f(_program_transforms, 1, true, ref view);
GL.Uniform1i(_program_image, 0);
GL.DrawArrays(PrimitiveType.Triangles, 0, 6);
GL.DeleteBuffer(buffer);
} }
public void Rectangle(in RectangleDrawInfo rectangle) public void Rectangle(in RectangleDrawInfo rectangle)
@@ -191,30 +147,12 @@ namespace Dashboard.OpenGL.Drawing
new ImmediateVertex(new Vector3(rectangle.Min.X, rectangle.Max.Y, depth), new Vector2(uv.Min.X, uv.Max.Y), Vector4.One), new ImmediateVertex(new Vector3(rectangle.Min.X, rectangle.Max.Y, depth), new Vector2(uv.Min.X, uv.Max.Y), Vector4.One),
]; ];
int buffer = GL.GenBuffer(); DrawImmediate(new ImmediateDrawCall(MeshPrimitive.Triangle, vertices.ToArray())
GL.BindBuffer(BufferTarget.ArrayBuffer, buffer); {
GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * ImmediateVertex.Size, ref vertices[0], BufferUsage.StreamDraw); Transforms = Context.ExtensionRequire<IDeviceContextBase>().Transforms,
PipelineState = new PipelineState { CullMode = FaceCulling.None },
GL.BindVertexArray(_vao); Texture = texture,
GL.VertexAttribPointer(_program_apos, 3, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.PosOffset); });
GL.EnableVertexAttribArray(_program_apos);
GL.VertexAttribPointer(_program_atexcoord, 2, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.TexCoordsOffset);
GL.EnableVertexAttribArray(_program_atexcoord);
GL.VertexAttribPointer(_program_acolor, 4, VertexAttribPointerType.Float, false, ImmediateVertex.Size, ImmediateVertex.ColorOffset);
GL.EnableVertexAttribArray(_program_acolor);
Matrix4x4 view = Context.ExtensionRequire<IDeviceContextBase>().Transforms;
GL.UseProgram(_program);
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, ((GLTexture)texture).Handle);
GL.UniformMatrix4f(_program_transforms, 1, true, ref view);
GL.Uniform1i(_program_image, 0);
GL.DrawArrays(PrimitiveType.Triangles, 0, 6);
GL.DeleteBuffer(buffer);
} }
IContextBase IContextExtensionBase.Context => Context; IContextBase IContextExtensionBase.Context => Context;
@@ -224,17 +162,53 @@ namespace Dashboard.OpenGL.Drawing
Require((DeviceContext)context); Require((DeviceContext)context);
} }
[StructLayout(LayoutKind.Explicit, Pack = sizeof(float) * 4, Size = Size)] public void DrawImmediate(ImmediateDrawCall call)
private struct ImmediateVertex(Vector3 position, Vector2 texCoords, Vector4 color)
{ {
[FieldOffset(PosOffset)] public Vector3 Position = position; // This is a terrible implementation as it stands but we can improve this immensely later on.
[FieldOffset(TexCoordsOffset)] public Vector2 TexCoords = texCoords; IDirectRendering dr = Context.ExtensionRequire<IDirectRendering>();
[FieldOffset(ColorOffset)] public Vector4 Color = color;
public const int Size = 16 * sizeof(float); int size = call.Vertices.Length * ImmediateVertex.Size;
public const int PosOffset = 0 * sizeof(float);
public const int TexCoordsOffset = 4 * sizeof(float); IBuffer buffer = dr.CreateBuffer(BufferAccessPattern.Stream, size);
public const int ColorOffset = 8 * sizeof(float); buffer.Write(0, call.Vertices.Span);
VertexSpecification spec = CreateVertexSpec(buffer);
ReadOnlyMemory<byte> uniforms = MemoryMarshal.AsBytes(stackalloc Matrix4x4[] {call.Transforms}).ToArray();
DrawCall drawCall = new DrawCall(call.Primitive, spec, 0, call.Vertices.Length)
{
ShaderPipeline = _program,
PipelineState = call.PipelineState,
UniformData = uniforms,
Uniforms = [
new UniformDescriptor() {
Location = _program_transforms,
Type = Dashboard.Drawing.UniformType.Mat4,
Offset = 0,
Size = Unsafe.SizeOf<Matrix4x4>(),
}
],
Textures = [call.Texture ?? _white]
};
dr.Draw(drawCall);
buffer.Dispose();
}
private VertexSpecification CreateVertexSpec(IBuffer buffer)
{
return new VertexSpecification(
[
new VertexAttribute((int)_program_apos, 3, VertexAttributeType.Float, ImmediateVertex.PosOffset, ImmediateVertex.Size),
new VertexAttribute((int)_program_atexcoord, 2, VertexAttributeType.Float, ImmediateVertex.TexCoordsOffset, ImmediateVertex.Size),
new VertexAttribute((int)_program_acolor, 4, VertexAttributeType.Float, ImmediateVertex.ColorOffset, ImmediateVertex.Size)
],
[
buffer,
buffer,
buffer
]
);
} }
} }
} }
+1 -1
View File
@@ -10,7 +10,7 @@ out vec2 vTexCoords;
out vec4 vColor; out vec4 vColor;
void main() { void main() {
vec4 position = vec4(aPos, 1.0) * transforms; vec4 position = transforms * vec4(aPos, 1.0);
gl_Position = position; gl_Position = position;
vTexCoords = aTexCoords; vTexCoords = aTexCoords;
+2 -2
View File
@@ -11,11 +11,11 @@ namespace Dashboard.OpenGL
public BufferAccessPattern AccessPattern { get; } = pattern; public BufferAccessPattern AccessPattern { get; } = pattern;
public long Size { get; private set; } = size; public long Size { get; private set; } = size;
public long Offset { get; private set; } = offset; public long Offset { get; private set; } = offset;
public int Handle { get; private set; } public int Handle { get; private set; } = handle;
public void Reallocate(long newSize) public void Reallocate(long newSize)
{ {
GL.CreateBuffer(out int handle); GL.GenBuffer(out int handle);
GL.BindBuffer(BufferTarget.ArrayBuffer, handle); GL.BindBuffer(BufferTarget.ArrayBuffer, handle);
GL.BufferData(BufferTarget.ArrayBuffer, (nint)newSize, (nint)0, AccessPattern switch GL.BufferData(BufferTarget.ArrayBuffer, (nint)newSize, (nint)0, AccessPattern switch
{ {
-1
View File
@@ -72,7 +72,6 @@ namespace Dashboard.OpenGL
Extensions = extensions.ToImmutableHashSet(); Extensions = extensions.ToImmutableHashSet();
ExtensionPreload<DeviceContextBase>(); ExtensionPreload<DeviceContextBase>();
ExtensionPreload<GLTextureExtension>();
ExtensionPreload<ImmediateMode>(); ExtensionPreload<ImmediateMode>();
ExtensionPreload<DirectRendering>(); ExtensionPreload<DirectRendering>();
} }
+4 -5
View File
@@ -9,7 +9,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{9D6CCC74
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dashboard.TestApplication", "tests\Dashboard.TestApplication\Dashboard.TestApplication.csproj", "{7C90B90B-DF31-439B-9080-CD805383B014}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dashboard.TestApplication", "tests\Dashboard.TestApplication\Dashboard.TestApplication.csproj", "{7C90B90B-DF31-439B-9080-CD805383B014}"
ProjectSection(ProjectDependencies) = postProject ProjectSection(ProjectDependencies) = postProject
{1BDFEF50-C907-42C8-B63B-E4F6F585CFB5} = {1BDFEF50-C907-42C8-B63B-E4F6F585CFB5}
{49A62F46-AC1C-4240-8615-020D4FBBF964} = {49A62F46-AC1C-4240-8615-020D4FBBF964} {49A62F46-AC1C-4240-8615-020D4FBBF964} = {49A62F46-AC1C-4240-8615-020D4FBBF964}
EndProjectSection EndProjectSection
EndProject EndProject
@@ -17,15 +16,15 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Common", "Dashboa
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Frameworks", "Frameworks", "{9B62A92D-ABF5-4704-B831-FD075515A82F}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Frameworks", "Frameworks", "{9B62A92D-ABF5-4704-B831-FD075515A82F}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.OpenTK", "Dashboard.OpenTK\Dashboard.OpenTK.csproj", "{7B064228-2629-486E-95C6-BDDD4B4602C4}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.OpenTK", "Frameworks\Dashboard.OpenTK\Dashboard.OpenTK.csproj", "{7B064228-2629-486E-95C6-BDDD4B4602C4}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.OpenGL", "Dashboard.OpenGL\Dashboard.OpenGL.csproj", "{33EB657C-B53A-41B4-BC3C-F38C09ABA577}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.OpenGL", "Dashboard.OpenGL\Dashboard.OpenGL.csproj", "{33EB657C-B53A-41B4-BC3C-F38C09ABA577}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.StbImage", "Dashboard.StbImage\Dashboard.StbImage.csproj", "{85BCEB9E-DEC2-4A53-B2DA-6BFC6F3EE4E7}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.StbImage", "Frameworks\Dashboard.StbImage\Dashboard.StbImage.csproj", "{85BCEB9E-DEC2-4A53-B2DA-6BFC6F3EE4E7}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.BlurgText.OpenGL", "Dashboard.BlurgText.OpenGL\Dashboard.BlurgText.OpenGL.csproj", "{14616F42-663B-4673-8561-5637FAD1B22F}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.BlurgText.OpenGL", "Frameworks\Dashboard.BlurgText.OpenGL\Dashboard.BlurgText.OpenGL.csproj", "{14616F42-663B-4673-8561-5637FAD1B22F}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.BlurgText", "Dashboard.BlurgText\Dashboard.BlurgText.csproj", "{8C68EFB6-B477-48EC-9AAA-31E89883482B}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.BlurgText", "Frameworks\Dashboard.BlurgText\Dashboard.BlurgText.csproj", "{8C68EFB6-B477-48EC-9AAA-31E89883482B}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
+144
View File
@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32pt"
height="32pt"
viewBox="0 0 11.288889 11.288889"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="active.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="pt"
showguides="false"
inkscape:lockguides="true"
inkscape:zoom="27.070313"
inkscape:cx="21.314863"
inkscape:cy="21.333333"
inkscape:window-width="2560"
inkscape:window-height="1364"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="0,1"
id="guide1"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="-1,0"
id="guide2"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,7.761111"
orientation="-1,0"
id="guide3"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,3.5277777"
orientation="-1,0"
id="guide4"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,7.761111"
orientation="0,1"
id="guide5"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,3.5277777"
orientation="0,1"
id="guide6"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,9.172222"
orientation="0,1"
id="guide7"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,2.1166666"
orientation="0,1"
id="guide8"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,2.1166666"
orientation="-1,0"
id="guide12"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,9.172222"
orientation="-1,0"
id="guide13"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="0,11.288889"
orientation="0,42.666667"
id="guide14"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,11.288889"
orientation="42.666667,0"
id="guide15"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,0"
orientation="0,-42.666667"
id="guide16"
inkscape:locked="true" />
<sodipodi:guide
position="0,0"
orientation="-42.666667,0"
id="guide17"
inkscape:locked="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#7399e5;fill-opacity:1;stroke:#6080bf;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18"
width="6.3499999"
height="6.3499999"
x="2.4694443"
y="2.4694443"
rx="0.70555556"
ry="0.70555556" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

+142
View File
@@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32pt"
height="32pt"
viewBox="0 0 11.288889 11.288889"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="bg.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="pt"
showguides="true"
inkscape:lockguides="true"
inkscape:zoom="27.070313"
inkscape:cx="20.372871"
inkscape:cy="25.359884"
inkscape:window-width="2560"
inkscape:window-height="1364"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="0,1"
id="guide1"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="-1,0"
id="guide2"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,7.761111"
orientation="-1,0"
id="guide3"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,3.5277777"
orientation="-1,0"
id="guide4"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,7.761111"
orientation="0,1"
id="guide5"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,3.5277777"
orientation="0,1"
id="guide6"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,9.172222"
orientation="0,1"
id="guide7"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,2.1166666"
orientation="0,1"
id="guide8"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,2.1166666"
orientation="-1,0"
id="guide12"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,9.172222"
orientation="-1,0"
id="guide13"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="0,11.288889"
orientation="0,42.666667"
id="guide14"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,11.288889"
orientation="42.666667,0"
id="guide15"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,0"
orientation="0,-42.666667"
id="guide16"
inkscape:locked="true" />
<sodipodi:guide
position="0,0"
orientation="-42.666667,0"
id="guide17"
inkscape:locked="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#b8bfcc;stroke:none;stroke-width:0.35277777;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers;stroke-dasharray:none;fill-opacity:1"
id="rect1"
width="11.288889"
height="11.288889"
x="0"
y="-3.3643511e-07" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

+144
View File
@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32pt"
height="32pt"
viewBox="0 0 11.288889 11.288889"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="button.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="pt"
showguides="false"
inkscape:lockguides="true"
inkscape:zoom="1"
inkscape:cx="22"
inkscape:cy="21.5"
inkscape:window-width="2560"
inkscape:window-height="1364"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="0,1"
id="guide1"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="-1,0"
id="guide2"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,7.761111"
orientation="-1,0"
id="guide3"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,3.5277777"
orientation="-1,0"
id="guide4"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,7.761111"
orientation="0,1"
id="guide5"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,3.5277777"
orientation="0,1"
id="guide6"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,9.172222"
orientation="0,1"
id="guide7"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,2.1166666"
orientation="0,1"
id="guide8"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,2.1166666"
orientation="-1,0"
id="guide12"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,9.172222"
orientation="-1,0"
id="guide13"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="0,11.288889"
orientation="0,42.666667"
id="guide14"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,11.288889"
orientation="42.666667,0"
id="guide15"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,0"
orientation="0,-42.666667"
id="guide16"
inkscape:locked="true" />
<sodipodi:guide
position="0,0"
orientation="-42.666667,0"
id="guide17"
inkscape:locked="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#e5e5e5;fill-opacity:1;stroke:#bfbfbf;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18"
width="6.3499999"
height="6.3499999"
x="2.4694443"
y="2.4694443"
rx="0.70555556"
ry="0.70555556" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

+153
View File
@@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32pt"
height="32pt"
viewBox="0 0 11.288889 11.288889"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="checked.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="pt"
showguides="true"
inkscape:lockguides="true"
inkscape:zoom="27.070313"
inkscape:cx="21.314863"
inkscape:cy="21.333333"
inkscape:window-width="2560"
inkscape:window-height="1364"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="0,1"
id="guide1"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="-1,0"
id="guide2"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,7.761111"
orientation="-1,0"
id="guide3"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,3.5277777"
orientation="-1,0"
id="guide4"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,7.761111"
orientation="0,1"
id="guide5"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,3.5277777"
orientation="0,1"
id="guide6"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,9.172222"
orientation="0,1"
id="guide7"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,2.1166666"
orientation="0,1"
id="guide8"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,2.1166666"
orientation="-1,0"
id="guide12"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,9.172222"
orientation="-1,0"
id="guide13"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="0,11.288889"
orientation="0,42.666667"
id="guide14"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,11.288889"
orientation="42.666667,0"
id="guide15"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,0"
orientation="0,-42.666667"
id="guide16"
inkscape:locked="true" />
<sodipodi:guide
position="0,0"
orientation="-42.666667,0"
id="guide17"
inkscape:locked="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#e5e5e5;fill-opacity:1;stroke:#bfbfbf;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18"
width="2.8222222"
height="2.8222222"
x="4.2333331"
y="4.2333331"
rx="0.70555556"
ry="0.70555556" />
<rect
style="fill:#00bf3a;fill-opacity:1;stroke:#008024;stroke-width:0.352778;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect19"
width="1.7638888"
height="1.7638888"
x="4.7625003"
y="4.7624998"
rx="0.26458332"
ry="0.26458332" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

+314
View File
@@ -0,0 +1,314 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="300pt"
height="200pt"
viewBox="0 0 105.83333 70.555555"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="concept.svg"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#b8bfcc"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="pt"
inkscape:zoom="4.265"
inkscape:cx="200"
inkscape:cy="133.29426"
inkscape:window-width="2560"
inkscape:window-height="1364"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showgrid="false"
showguides="true"><inkscape:grid
id="grid1"
units="pt"
originx="5.6444444"
originy="5.6444444"
spacingx="11.288889"
spacingy="11.288889"
empcolor="#0099e5"
empopacity="0.30196078"
color="#0099e5"
opacity="0.14901961"
empspacing="5"
enabled="true"
visible="false" /></sodipodi:namedview><defs
id="defs1" /><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"><rect
style="fill:#e5e5e5;fill-opacity:1;stroke:#6080bf;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18"
width="12.491558"
height="6.3499985"
x="89.838898"
y="58.913887"
rx="0.70555556"
ry="0.70555556" /><text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans';text-align:center;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#b8bfcc;stroke-width:0.352777;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="95.955559"
y="63.676388"
id="text1"><tspan
sodipodi:role="line"
id="tspan1"
style="fill:#666666;stroke-width:0.352778;-inkscape-font-specification:'Noto Sans';font-family:'Noto Sans';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="95.955559"
y="63.676388">Cont</tspan></text><rect
style="fill:#e5e5e5;fill-opacity:1;stroke:#6080bf;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18-5"
width="12.491558"
height="6.3499985"
x="89.709778"
y="2.469445"
rx="0.70555556"
ry="0.70555556" /><text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans';text-align:center;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#b8bfcc;stroke-width:0.352777;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="95.826447"
y="7.231946"
id="text1-9"><tspan
sodipodi:role="line"
id="tspan1-2"
style="fill:#666666;stroke-width:0.352778;-inkscape-font-specification:'Noto Sans';font-family:'Noto Sans';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="95.826447"
y="7.231946">...</tspan></text><rect
style="fill:#e5e5e5;fill-opacity:1;stroke:#bfbfbf;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect1"
width="12.491558"
height="6.3499985"
x="72.897102"
y="58.913887"
rx="0.70555556"
ry="0.70555556" /><text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans';text-align:center;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#b8bfcc;stroke-width:0.352777;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="79.013763"
y="63.676388"
id="text2"><tspan
sodipodi:role="line"
id="tspan2"
style="fill:#666666;stroke-width:0.352778;-inkscape-font-specification:'Noto Sans';font-family:'Noto Sans';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="79.013763"
y="63.676388">Retry</tspan></text><rect
style="fill:#e5e5e5;fill-opacity:1;stroke:#bfbfbf;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect2"
width="12.491558"
height="6.3499985"
x="55.843109"
y="58.913887"
rx="0.70555556"
ry="0.70555556" /><text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans';text-align:center;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#b8bfcc;stroke-width:0.352777;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="61.95977"
y="63.676388"
id="text3"><tspan
sodipodi:role="line"
id="tspan3"
style="fill:#666666;stroke-width:0.352778;-inkscape-font-specification:'Noto Sans';font-family:'Noto Sans';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="61.95977"
y="63.676388">Abort</tspan></text><rect
style="fill:#a6a6a6;fill-opacity:1;stroke:#808080;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18-9"
width="97.24514"
height="5.6444435"
x="5.6444445"
y="47.977776"
rx="2.8222222"
ry="2.8222218" /><rect
style="fill:#00bf3a;fill-opacity:1;stroke:#008024;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18-93"
width="43.647232"
height="5.6444435"
x="5.6444445"
y="47.977776"
rx="2.8222222"
ry="2.8222218" /><rect
style="fill:#a6a6a6;fill-opacity:1;stroke:#808080;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect3"
width="97.24514"
height="5.6444435"
x="5.6444445"
y="36.688889"
rx="2.8222222"
ry="2.8222218" /><rect
style="fill:#405580;fill-opacity:1;stroke:#324466;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18-6"
width="79.93454"
height="5.6444435"
x="5.6444445"
y="36.688889"
rx="2.8222222"
ry="2.8222218" /><text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans';text-align:center;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#e6e6e6;stroke-width:0.352777;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="54.267017"
y="40.850075"
id="text4"><tspan
sodipodi:role="line"
id="tspan4"
style="fill:#e6e6e6;stroke-width:0.352778;-inkscape-font-specification:'Noto Sans';font-family:'Noto Sans';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="54.267017"
y="40.850075">Compression 8</tspan></text><text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans';text-align:center;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:middle;fill:#e6e6e6;stroke-width:0.352777;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="54.267017"
y="52.356026"
id="text5"><tspan
sodipodi:role="line"
id="tspan5"
style="fill:#e6e6e6;stroke-width:0.352778;-inkscape-font-specification:'Noto Sans';font-family:'Noto Sans';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="54.267017"
y="52.356026">100 MB/s</tspan></text><rect
style="fill:#fffbe5;fill-opacity:1;stroke:#e5e5e5;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18-2"
width="81.550797"
height="6.3500004"
x="6.0325446"
y="2.469445"
rx="0.70555556"
ry="0.70555556" /><text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans';text-align:start;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:start;fill:#b8bfcc;stroke-width:0.352777;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="7.9346051"
y="7.1507883"
id="text6"><tspan
sodipodi:role="line"
id="tspan6"
style="fill:#666666;stroke-width:0.352778;-inkscape-font-specification:'Noto Sans';font-family:'Noto Sans';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="7.9346051"
y="7.1507883">Path/Inside/The/Computer</tspan></text><g
inkscape:label="Layer 1"
id="layer1-7"
transform="translate(4.127445e-7,11.288889)"><rect
style="fill:#e5e5e5;fill-opacity:1;stroke:#bfbfbf;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18-3"
width="2.8222222"
height="2.8222222"
x="4.2333331"
y="4.2333331"
rx="0.70555556"
ry="0.70555556" /><rect
style="fill:#00bf3a;fill-opacity:1;stroke:#008024;stroke-width:0.352778;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect19"
width="1.7638888"
height="1.7638888"
x="4.7625003"
y="4.7624998"
rx="0.26458332"
ry="0.26458332" /></g><g
inkscape:label="Layer 1"
id="layer1-9"
transform="translate(45.155557,11.288888)"><circle
style="fill:#e5e5e5;fill-opacity:1;stroke:#bfbfbf;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path17"
cx="5.6444445"
cy="5.6444445"
r="1.4111111" /><circle
style="fill:#00bf3a;fill-opacity:1;stroke:#008024;stroke-width:0.352778;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path18"
cx="5.6444445"
cy="5.6444445"
r="0.88194442" /></g><circle
style="fill:#e5e5e5;fill-opacity:1;stroke:#bfbfbf;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path17-7"
cx="50.800003"
cy="28.222221"
r="1.4111111" /><rect
style="fill:#e5e5e5;fill-opacity:1;stroke:#bfbfbf;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18-36"
width="2.8222222"
height="2.8222222"
x="4.1698418"
y="21.166666"
rx="0.70555556"
ry="0.70555556" /><g
inkscape:label="Layer 1"
id="g7"
transform="translate(4.127445e-7,11.288889)"><rect
style="fill:#e5e5e5;fill-opacity:1;stroke:#bfbfbf;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect6"
width="2.8222222"
height="2.8222222"
x="4.2333331"
y="4.2333331"
rx="0.70555556"
ry="0.70555556" /><rect
style="fill:#00bf3a;fill-opacity:1;stroke:#008024;stroke-width:0.352778;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect7"
width="1.7638888"
height="1.7638888"
x="4.7625003"
y="4.7624998"
rx="0.26458332"
ry="0.26458332" /></g><text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans';text-align:start;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:start;fill:#b8bfcc;stroke-width:0.352777;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="7.9346051"
y="18.329336"
id="text7"><tspan
sodipodi:role="line"
id="tspan7"
style="fill:#666666;stroke-width:0.352778;-inkscape-font-specification:'Noto Sans';font-family:'Noto Sans';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="7.9346051"
y="18.329336">Delete Files</tspan></text><text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans';text-align:start;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:start;fill:#b8bfcc;stroke-width:0.352777;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="7.9346051"
y="24.106392"
id="text8"><tspan
sodipodi:role="line"
id="tspan8"
style="fill:#666666;stroke-width:0.352778;-inkscape-font-specification:'Noto Sans';font-family:'Noto Sans';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="7.9346051"
y="24.106392">Kill all humans</tspan></text><circle
style="fill:#e5e5e5;fill-opacity:1;stroke:#bfbfbf;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="circle8"
cx="50.800003"
cy="22.577778"
r="1.4111111" /><text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans';text-align:start;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:start;fill:#b8bfcc;stroke-width:0.352777;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="53.707523"
y="18.329336"
id="text9"><tspan
sodipodi:role="line"
id="tspan9"
style="fill:#666666;stroke-width:0.352778;-inkscape-font-specification:'Noto Sans';font-family:'Noto Sans';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="53.707523"
y="18.329336">Option that does nothing</tspan></text><text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans';text-align:start;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:start;fill:#b8bfcc;stroke-width:0.352777;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="53.707523"
y="24.106392"
id="text10"><tspan
sodipodi:role="line"
id="tspan10"
style="fill:#666666;stroke-width:0.352778;-inkscape-font-specification:'Noto Sans';font-family:'Noto Sans';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="53.707523"
y="24.106392">Diogenes mode</tspan></text><text
xml:space="preserve"
style="font-size:4.23333px;line-height:normal;font-family:'Noto Sans';-inkscape-font-specification:'Noto Sans';text-align:start;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;direction:ltr;text-orientation:upright;text-anchor:start;fill:#b8bfcc;stroke-width:0.352777;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="53.707523"
y="30.059517"
id="text11"><tspan
sodipodi:role="line"
id="tspan11"
style="fill:#666666;stroke-width:0.352778;-inkscape-font-specification:'Noto Sans';font-family:'Noto Sans';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
x="53.707523"
y="30.059517">pringle</tspan></text></g></svg>

After

Width:  |  Height:  |  Size: 18 KiB

+144
View File
@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32pt"
height="32pt"
viewBox="0 0 11.288889 11.288889"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="group.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="pt"
showguides="true"
inkscape:lockguides="true"
inkscape:zoom="27.070313"
inkscape:cx="22.219913"
inkscape:cy="21.296392"
inkscape:window-width="2560"
inkscape:window-height="1364"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="0,1"
id="guide1"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="-1,0"
id="guide2"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,7.761111"
orientation="-1,0"
id="guide3"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,3.5277777"
orientation="-1,0"
id="guide4"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,7.761111"
orientation="0,1"
id="guide5"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,3.5277777"
orientation="0,1"
id="guide6"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,9.172222"
orientation="0,1"
id="guide7"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,2.1166666"
orientation="0,1"
id="guide8"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,2.1166666"
orientation="-1,0"
id="guide12"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,9.172222"
orientation="-1,0"
id="guide13"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="0,11.288889"
orientation="0,42.666667"
id="guide14"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,11.288889"
orientation="42.666667,0"
id="guide15"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,0"
orientation="0,-42.666667"
id="guide16"
inkscape:locked="true" />
<sodipodi:guide
position="0,0"
orientation="-42.666667,0"
id="guide17"
inkscape:locked="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;fill-opacity:1;stroke:#898f99;stroke-width:0.35277777;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18"
width="7.7611108"
height="7.7611108"
x="1.7638892"
y="1.7638892"
rx="0.70555556"
ry="0.70555556" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

+144
View File
@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32pt"
height="32pt"
viewBox="0 0 11.288889 11.288889"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="hover.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="pt"
showguides="false"
inkscape:lockguides="true"
inkscape:zoom="27.070313"
inkscape:cx="21.314863"
inkscape:cy="21.333333"
inkscape:window-width="2560"
inkscape:window-height="1364"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="0,1"
id="guide1"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="-1,0"
id="guide2"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,7.761111"
orientation="-1,0"
id="guide3"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,3.5277777"
orientation="-1,0"
id="guide4"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,7.761111"
orientation="0,1"
id="guide5"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,3.5277777"
orientation="0,1"
id="guide6"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,9.172222"
orientation="0,1"
id="guide7"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,2.1166666"
orientation="0,1"
id="guide8"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,2.1166666"
orientation="-1,0"
id="guide12"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,9.172222"
orientation="-1,0"
id="guide13"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="0,11.288889"
orientation="0,42.666667"
id="guide14"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,11.288889"
orientation="42.666667,0"
id="guide15"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,0"
orientation="0,-42.666667"
id="guide16"
inkscape:locked="true" />
<sodipodi:guide
position="0,0"
orientation="-42.666667,0"
id="guide17"
inkscape:locked="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#e5e5e5;fill-opacity:1;stroke:#6080bf;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18"
width="6.3499999"
height="6.3499999"
x="2.4694443"
y="2.4694443"
rx="0.70555556"
ry="0.70555556" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

+144
View File
@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32pt"
height="32pt"
viewBox="0 0 11.288889 11.288889"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="input.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="pt"
showguides="false"
inkscape:lockguides="true"
inkscape:zoom="27.070313"
inkscape:cx="21.333333"
inkscape:cy="21.333333"
inkscape:window-width="2560"
inkscape:window-height="1364"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="0,1"
id="guide1"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="-1,0"
id="guide2"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,7.761111"
orientation="-1,0"
id="guide3"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,3.5277777"
orientation="-1,0"
id="guide4"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,7.761111"
orientation="0,1"
id="guide5"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,3.5277777"
orientation="0,1"
id="guide6"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,9.172222"
orientation="0,1"
id="guide7"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,2.1166666"
orientation="0,1"
id="guide8"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,2.1166666"
orientation="-1,0"
id="guide12"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,9.172222"
orientation="-1,0"
id="guide13"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="0,11.288889"
orientation="0,42.666667"
id="guide14"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,11.288889"
orientation="42.666667,0"
id="guide15"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,0"
orientation="0,-42.666667"
id="guide16"
inkscape:locked="true" />
<sodipodi:guide
position="0,0"
orientation="-42.666667,0"
id="guide17"
inkscape:locked="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#fffbe5;fill-opacity:1;stroke:#e5e5e5;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18"
width="6.3499999"
height="6.3499999"
x="2.4694443"
y="2.4694443"
rx="0.70555556"
ry="0.70555556" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

+144
View File
@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32pt"
height="32pt"
viewBox="0 0 11.288889 11.288889"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="noprog.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="pt"
showguides="true"
inkscape:lockguides="true"
inkscape:zoom="22.627417"
inkscape:cx="17.987029"
inkscape:cy="22.119184"
inkscape:window-width="2560"
inkscape:window-height="1364"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="0,1"
id="guide1"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="-1,0"
id="guide2"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,7.761111"
orientation="-1,0"
id="guide3"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,3.5277777"
orientation="-1,0"
id="guide4"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,7.761111"
orientation="0,1"
id="guide5"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,3.5277777"
orientation="0,1"
id="guide6"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,9.172222"
orientation="0,1"
id="guide7"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,2.1166666"
orientation="0,1"
id="guide8"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,2.1166666"
orientation="-1,0"
id="guide12"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,9.172222"
orientation="-1,0"
id="guide13"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="0,11.288889"
orientation="0,42.666667"
id="guide14"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,11.288889"
orientation="42.666667,0"
id="guide15"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,0"
orientation="0,-42.666667"
id="guide16"
inkscape:locked="true" />
<sodipodi:guide
position="0,0"
orientation="-42.666667,0"
id="guide17"
inkscape:locked="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#a6a6a6;fill-opacity:1;stroke:#808080;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18"
width="8.4666662"
height="5.6444445"
x="1.411111"
y="2.822222"
rx="2.8222222"
ry="2.8222222" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

+144
View File
@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32pt"
height="32pt"
viewBox="0 0 11.288889 11.288889"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="progrs.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="pt"
showguides="true"
inkscape:lockguides="true"
inkscape:zoom="27.070313"
inkscape:cx="21.314863"
inkscape:cy="21.333333"
inkscape:window-width="2560"
inkscape:window-height="1364"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="0,1"
id="guide1"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="-1,0"
id="guide2"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,7.761111"
orientation="-1,0"
id="guide3"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,3.5277777"
orientation="-1,0"
id="guide4"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,7.761111"
orientation="0,1"
id="guide5"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,3.5277777"
orientation="0,1"
id="guide6"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,9.172222"
orientation="0,1"
id="guide7"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,2.1166666"
orientation="0,1"
id="guide8"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,2.1166666"
orientation="-1,0"
id="guide12"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,9.172222"
orientation="-1,0"
id="guide13"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="0,11.288889"
orientation="0,42.666667"
id="guide14"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,11.288889"
orientation="42.666667,0"
id="guide15"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,0"
orientation="0,-42.666667"
id="guide16"
inkscape:locked="true" />
<sodipodi:guide
position="0,0"
orientation="-42.666667,0"
id="guide17"
inkscape:locked="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#00bf3a;fill-opacity:1;stroke:#008024;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18"
width="8.4666662"
height="5.6444445"
x="1.411111"
y="2.822222"
rx="2.8222222"
ry="2.8222222" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

+147
View File
@@ -0,0 +1,147 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32pt"
height="32pt"
viewBox="0 0 11.288889 11.288889"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="radio_checked.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="pt"
showguides="true"
inkscape:lockguides="true"
inkscape:zoom="27.070313"
inkscape:cx="21.314863"
inkscape:cy="21.333333"
inkscape:window-width="2560"
inkscape:window-height="1364"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="0,1"
id="guide1"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="-1,0"
id="guide2"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,7.761111"
orientation="-1,0"
id="guide3"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,3.5277777"
orientation="-1,0"
id="guide4"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,7.761111"
orientation="0,1"
id="guide5"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,3.5277777"
orientation="0,1"
id="guide6"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,9.172222"
orientation="0,1"
id="guide7"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,2.1166666"
orientation="0,1"
id="guide8"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,2.1166666"
orientation="-1,0"
id="guide12"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,9.172222"
orientation="-1,0"
id="guide13"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="0,11.288889"
orientation="0,42.666667"
id="guide14"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,11.288889"
orientation="42.666667,0"
id="guide15"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,0"
orientation="0,-42.666667"
id="guide16"
inkscape:locked="true" />
<sodipodi:guide
position="0,0"
orientation="-42.666667,0"
id="guide17"
inkscape:locked="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<circle
style="fill:#e5e5e5;fill-opacity:1;stroke:#bfbfbf;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path17"
cx="5.6444445"
cy="5.6444445"
r="1.4111111" />
<circle
style="fill:#00bf3a;fill-opacity:1;stroke:#008024;stroke-width:0.352778;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path18"
cx="5.6444445"
cy="5.6444445"
r="0.88194442" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

+141
View File
@@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32pt"
height="32pt"
viewBox="0 0 11.288889 11.288889"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="radio.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="pt"
showguides="true"
inkscape:lockguides="true"
inkscape:zoom="27.070313"
inkscape:cx="20.188167"
inkscape:cy="20.631457"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="0,1"
id="guide1"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="-1,0"
id="guide2"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,7.761111"
orientation="-1,0"
id="guide3"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,3.5277777"
orientation="-1,0"
id="guide4"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,7.761111"
orientation="0,1"
id="guide5"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,3.5277777"
orientation="0,1"
id="guide6"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,9.172222"
orientation="0,1"
id="guide7"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,2.1166666"
orientation="0,1"
id="guide8"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,2.1166666"
orientation="-1,0"
id="guide12"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,9.172222"
orientation="-1,0"
id="guide13"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="0,11.288889"
orientation="0,42.666667"
id="guide14"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,11.288889"
orientation="42.666667,0"
id="guide15"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,0"
orientation="0,-42.666667"
id="guide16"
inkscape:locked="true" />
<sodipodi:guide
position="0,0"
orientation="-42.666667,0"
id="guide17"
inkscape:locked="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<circle
style="fill:#e5e5e5;fill-opacity:1;stroke:#bfbfbf;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path17"
cx="5.6444445"
cy="5.6444445"
r="1.4111111" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

+144
View File
@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32pt"
height="32pt"
viewBox="0 0 11.288889 11.288889"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="slider.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="pt"
showguides="true"
inkscape:lockguides="true"
inkscape:zoom="19.141602"
inkscape:cx="13.217285"
inkscape:cy="25.938268"
inkscape:window-width="2560"
inkscape:window-height="1364"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="0,1"
id="guide1"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="-1,0"
id="guide2"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,7.761111"
orientation="-1,0"
id="guide3"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,3.5277777"
orientation="-1,0"
id="guide4"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,7.761111"
orientation="0,1"
id="guide5"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,3.5277777"
orientation="0,1"
id="guide6"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,9.172222"
orientation="0,1"
id="guide7"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,2.1166666"
orientation="0,1"
id="guide8"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,2.1166666"
orientation="-1,0"
id="guide12"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,9.172222"
orientation="-1,0"
id="guide13"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="0,11.288889"
orientation="0,42.666667"
id="guide14"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,11.288889"
orientation="42.666667,0"
id="guide15"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,0"
orientation="0,-42.666667"
id="guide16"
inkscape:locked="true" />
<sodipodi:guide
position="0,0"
orientation="-42.666667,0"
id="guide17"
inkscape:locked="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#405580;fill-opacity:1;stroke:#324466;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18"
width="8.4666662"
height="5.6444445"
x="1.411111"
y="2.822222"
rx="2.8222222"
ry="2.8222222" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

+134
View File
@@ -0,0 +1,134 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32pt"
height="32pt"
viewBox="0 0 11.288889 11.288889"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="template.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="pt"
showguides="true"
inkscape:lockguides="true"
inkscape:zoom="27.070313"
inkscape:cx="20.354401"
inkscape:cy="20.890043"
inkscape:window-width="2560"
inkscape:window-height="1364"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="0,1"
id="guide1"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="-1,0"
id="guide2"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,7.761111"
orientation="-1,0"
id="guide3"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,3.5277777"
orientation="-1,0"
id="guide4"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,7.761111"
orientation="0,1"
id="guide5"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,3.5277777"
orientation="0,1"
id="guide6"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,9.172222"
orientation="0,1"
id="guide7"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,2.1166666"
orientation="0,1"
id="guide8"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,2.1166666"
orientation="-1,0"
id="guide12"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,9.172222"
orientation="-1,0"
id="guide13"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="0,11.288889"
orientation="0,42.666667"
id="guide14"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,11.288889"
orientation="42.666667,0"
id="guide15"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,0"
orientation="0,-42.666667"
id="guide16"
inkscape:locked="true" />
<sodipodi:guide
position="0,0"
orientation="-42.666667,0"
id="guide17"
inkscape:locked="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1" />
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

+144
View File
@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="32pt"
height="32pt"
viewBox="0 0 11.288889 11.288889"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="uncheck.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="pt"
showguides="true"
inkscape:lockguides="true"
inkscape:zoom="27.070313"
inkscape:cx="21.314863"
inkscape:cy="21.333333"
inkscape:window-width="2560"
inkscape:window-height="1364"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="0,1"
id="guide1"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="5.6444443,5.6444443"
orientation="-1,0"
id="guide2"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,7.761111"
orientation="-1,0"
id="guide3"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,3.5277777"
orientation="-1,0"
id="guide4"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="7.761111,7.761111"
orientation="0,1"
id="guide5"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="3.5277777,3.5277777"
orientation="0,1"
id="guide6"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,9.172222"
orientation="0,1"
id="guide7"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,2.1166666"
orientation="0,1"
id="guide8"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="2.1166666,2.1166666"
orientation="-1,0"
id="guide12"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="9.172222,9.172222"
orientation="-1,0"
id="guide13"
inkscape:locked="true"
inkscape:label=""
inkscape:color="rgb(0,134,229)" />
<sodipodi:guide
position="0,11.288889"
orientation="0,42.666667"
id="guide14"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,11.288889"
orientation="42.666667,0"
id="guide15"
inkscape:locked="true" />
<sodipodi:guide
position="11.288889,0"
orientation="0,-42.666667"
id="guide16"
inkscape:locked="true" />
<sodipodi:guide
position="0,0"
orientation="-42.666667,0"
id="guide17"
inkscape:locked="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#e5e5e5;fill-opacity:1;stroke:#bfbfbf;stroke-width:0.705556;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="rect18"
width="2.8222222"
height="2.8222222"
x="4.2333331"
y="4.2333331"
rx="0.70555556"
ry="0.70555556" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

@@ -4,6 +4,7 @@ using System.Runtime.InteropServices;
using BlurgText; using BlurgText;
using Dashboard.Drawing; using Dashboard.Drawing;
using Dashboard.OpenGL; using Dashboard.OpenGL;
using Dashboard.Pal;
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using OPENGL = OpenTK.Graphics.OpenGL; using OPENGL = OpenTK.Graphics.OpenGL;
@@ -21,7 +22,6 @@ namespace Dashboard.BlurgText.OpenGL
private int _vertexArray = 0; private int _vertexArray = 0;
public override Blurg Blurg { get; } public override Blurg Blurg { get; }
public bool SystemFontsEnabled { get; set; }
public bool IsDisposed { get; private set; } = false; public bool IsDisposed { get; private set; } = false;
public override string DriverName => "BlurgText"; public override string DriverName => "BlurgText";
@@ -33,7 +33,12 @@ namespace Dashboard.BlurgText.OpenGL
public BlurgGLExtension() public BlurgGLExtension()
{ {
Blurg = new Blurg(AllocateTexture, UpdateTexture); Blurg = new Blurg(AllocateTexture, UpdateTexture);
SystemFontsEnabled = Blurg.EnableSystemFonts(); }
public override void Require(DeviceContext context)
{
base.Require(context);
Application.ExtensionRequire<BlurgTextExtension>().Loader.Configure(Blurg);
} }
~BlurgGLExtension() ~BlurgGLExtension()
@@ -8,8 +8,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="../Dashboard.BlurgText/Dashboard.BlurgText.csproj"/> <ProjectReference Include="..\Dashboard.BlurgText/Dashboard.BlurgText.csproj"/>
<ProjectReference Include="..\Dashboard.OpenGL\Dashboard.OpenGL.csproj" /> <ProjectReference Include="..\..\Dashboard.OpenGL\Dashboard.OpenGL.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -6,20 +6,21 @@ using Dashboard.Pal;
namespace Dashboard.BlurgText namespace Dashboard.BlurgText
{ {
public interface IBlurgDcExtensionFactory public interface IBlurgTextLoader
{ {
public void Configure(Blurg blurg);
public BlurgDcExtension CreateExtension(BlurgTextExtension appExtension, DeviceContext dc); public BlurgDcExtension CreateExtension(BlurgTextExtension appExtension, DeviceContext dc);
} }
public class BlurgTextExtension(IBlurgDcExtensionFactory dcExtensionFactory) : IFontLoader public class BlurgTextExtension(IBlurgTextLoader loader) : IFontLoader
{ {
private readonly Blurg _blurg = new Blurg(GlobalTextureAllocation, GlobalTextureUpdate); private readonly Blurg _blurg = new Blurg(GlobalTextureAllocation, GlobalTextureUpdate);
public Application Context { get; private set; } = null!; public Application Context { get; private set; } = null!;
public string DriverName { get; } = "BlurgText"; public string DriverName { get; } = "BlurgText";
public string DriverVendor { get; } = "Dashbord and BlurgText"; public string DriverVendor { get; } = "Dashboard and BlurgText";
public Version DriverVersion { get; } = new Version(1, 0); public Version DriverVersion { get; } = new Version(1, 0);
public IBlurgDcExtensionFactory DcExtensionFactory { get; } = dcExtensionFactory; public IBlurgTextLoader Loader { get; } = loader;
IContextBase IContextExtensionBase.Context => Context; IContextBase IContextExtensionBase.Context => Context;
public bool IsDisposed { get; private set; } = false; public bool IsDisposed { get; private set; } = false;
@@ -27,14 +28,14 @@ namespace Dashboard.BlurgText
{ {
Context = context; Context = context;
context.DeviceContextCreated += OnDeviceContextCreated; context.DeviceContextCreated += OnDeviceContextCreated;
_blurg.EnableSystemFonts(); Loader.Configure(_blurg);
} }
void IContextExtensionBase.Require(IContextBase context) => Require((Application)context); void IContextExtensionBase.Require(IContextBase context) => Require((Application)context);
private void RequireDeviceContextExtension(DeviceContext dc) private void RequireDeviceContextExtension(DeviceContext dc)
{ {
dc.ExtensionPreload<BlurgDcExtension>(() => DcExtensionFactory.CreateExtension(this, dc)); dc.ExtensionPreload<BlurgDcExtension>(() => Loader.CreateExtension(this, dc));
} }
private void OnDeviceContextCreated(object? sender, DeviceContext dc) private void OnDeviceContextCreated(object? sender, DeviceContext dc)
@@ -189,7 +190,7 @@ namespace Dashboard.BlurgText
IContextBase IContextExtensionBase.Context => Context; IContextBase IContextExtensionBase.Context => Context;
public void Require(DeviceContext context) public virtual void Require(DeviceContext context)
{ {
Context = context; Context = context;
} }
@@ -8,8 +8,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BlurgText" Version="0.1.0-nightly-33" /> <PackageReference Include="BlurgText" Version="0.1.0" />
<ProjectReference Include="..\Dashboard.Common\Dashboard.Common.csproj" /> <ProjectReference Include="..\..\Dashboard.Common\Dashboard.Common.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -11,8 +11,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Dashboard.OpenGL\Dashboard.OpenGL.csproj" /> <ProjectReference Include="..\..\Dashboard.OpenGL\Dashboard.OpenGL.csproj" />
<ProjectReference Include="..\Dashboard\Dashboard.csproj" /> <ProjectReference Include="..\..\Dashboard\Dashboard.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -8,7 +8,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Dashboard.Common\Dashboard.Common.csproj" /> <ProjectReference Include="..\..\Dashboard.Common\Dashboard.Common.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -8,9 +8,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Dashboard.BlurgText.OpenGL\Dashboard.BlurgText.OpenGL.csproj" /> <ProjectReference Include="..\..\Frameworks\Dashboard.BlurgText.OpenGL\Dashboard.BlurgText.OpenGL.csproj" />
<ProjectReference Include="..\..\Dashboard.BlurgText\Dashboard.BlurgText.csproj" /> <ProjectReference Include="..\..\Frameworks\Dashboard.BlurgText\Dashboard.BlurgText.csproj" />
<ProjectReference Include="..\..\Dashboard.OpenTK\Dashboard.OpenTK.csproj" /> <ProjectReference Include="..\..\Frameworks\Dashboard.OpenTK\Dashboard.OpenTK.csproj" />
<ProjectReference Include="..\..\Dashboard.StbImage\Dashboard.StbImage.csproj" /> <ProjectReference Include="..\..\Frameworks\Dashboard.StbImage\Dashboard.StbImage.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+35 -50
View File
@@ -1,4 +1,5 @@
using Dashboard.BlurgText; using BlurgText;
using Dashboard.BlurgText;
using Dashboard.BlurgText.OpenGL; using Dashboard.BlurgText.OpenGL;
using Dashboard.Controls; using Dashboard.Controls;
using Dashboard.Drawing; using Dashboard.Drawing;
@@ -52,7 +53,7 @@ PhysicalWindow window = (PhysicalWindow)app.CreatePhysicalWindow();
MessageBox box = MessageBox.Create(window, "Are you sure you want to exit?", "Confirm Exit", MessageBoxIcon.Question, MessageBox box = MessageBox.Create(window, "Are you sure you want to exit?", "Confirm Exit", MessageBoxIcon.Question,
MessageBoxButtons.YesNo); MessageBoxButtons.YesNo);
// window.Title = "DashTerm"; window.Title = "DashTerm";
TK.Window.SetMinClientSize(window.WindowHandle, 300, 200); TK.Window.SetMinClientSize(window.WindowHandle, 300, 200);
TK.Window.SetClientSize(window.WindowHandle, new Vector2i(320, 240)); TK.Window.SetClientSize(window.WindowHandle, new Vector2i(320, 240));
TK.Window.SetBorderStyle(window.WindowHandle, WindowBorderStyle.ResizableBorder); TK.Window.SetBorderStyle(window.WindowHandle, WindowBorderStyle.ResizableBorder);
@@ -73,70 +74,54 @@ TK.Window.SetMode(window.WindowHandle, WindowMode.Normal);
window.DeviceContext.ExtensionRequire<IDeviceContextBase>().ScaleOverride = 1.5f; window.DeviceContext.ExtensionRequire<IDeviceContextBase>().ScaleOverride = 1.5f;
IDirectRendering direct = window.DeviceContext.ExtensionRequire<IDirectRendering>(); IDirectRendering direct = window.DeviceContext.ExtensionRequire<IDirectRendering>();
IBuffer vertex = direct.CreateBuffer(BufferAccessPattern.Static, 1024); IImmediateMode imm = window.DeviceContext.ExtensionRequire<IImmediateMode>();
float[] vertices = new float[] ImmediateVertex[] vertices = new ImmediateVertex[]
{ // x, y, z, _, r, g, b, a { // x, y, z, r, g, b, a
-0.5f, -0.5f, 0.0f, 0.0f, 1, 0, 0, 1, new ImmediateVertex(new System.Numerics.Vector3(-0.5f, -0.5f, 0.0f), System.Numerics.Vector2.Zero, new System.Numerics.Vector4(1, 0, 0, 1)),
+0.5f, -0.5f, 0.0f, 0.0f, 0, 1, 0, 1, new ImmediateVertex(new System.Numerics.Vector3(+0.5f, -0.5f, 0.0f), System.Numerics.Vector2.Zero, new System.Numerics.Vector4(0, 1, 0, 1)),
+0.0f, +0.5f, 0.0f, 0.0f, 0, 0, 1, 1, new ImmediateVertex(new System.Numerics.Vector3(+0.0f, +0.5f, 0.0f), System.Numerics.Vector2.Zero, new System.Numerics.Vector4(0, 0, 1, 1)),
}; };
vertex.Write(0, vertices);
IShader shader = direct.CreatePipeline(new GlslShaderCreateInfo()
{
VertexShader =
"""
#version 410
layout (location = 0) in vec3 apos;
layout (location = 1) in vec4 acolor;
out vec4 vcolor;
void main()
{
gl_Position = vec4(apos, 1);
vcolor = acolor;
}
""",
FragmentShader =
"""
#version 410
in vec4 vcolor;
out vec4 fcolor;
void main()
{
fcolor = vcolor;
}
""",
});
DrawCall call = new DrawCall(MeshPrimitive.Triangle, vertex, 0, 3)
{
ShaderPipeline = shader,
Attributes =
[
new VertexAttribute(0, 3, VertexAttributeType.Float, 0, 8 * sizeof(float)),
new VertexAttribute(1, 4, VertexAttributeType.Float, 4 * sizeof(float), 8 * sizeof(float))
],
};
window.EventRaised += (sender, eventArgs) => window.EventRaised += (sender, eventArgs) =>
{ {
if (eventArgs is not PaintEventArgs paint) if (eventArgs is not PaintEventArgs paint)
return; return;
paint.DeviceContext.ExtensionRequire<IDirectRendering>().Draw(call); // window.DeviceContext.Begin();
var dcb = window.DeviceContext.ExtensionRequire<IDeviceContextBase>();
dcb.ResetTransforms();
dcb.ClearDepth();
imm.Rectangle(
new Dashboard.Box2d(
new System.Numerics.Vector2(30, 30),
new System.Numerics.Vector2(140, 110)),
0.2f,
new System.Numerics.Vector4(1, 1, 0, 1));
imm.Line(
new System.Numerics.Vector2(180, 40),
new System.Numerics.Vector2(290, 180),
6,
0.2f,
new System.Numerics.Vector4(0, 1, 1, 1));
imm.DrawImmediate(new ImmediateDrawCall(MeshPrimitive.Triangle, vertices.AsMemory()));
// window.DeviceContext.End();
}; };
app.Run(true, source.Token); app.Run(true, source.Token);
class BlurgTextExtensionFactory : IBlurgDcExtensionFactory class BlurgTextExtensionFactory : IBlurgTextLoader
{ {
public BlurgDcExtension CreateExtension(BlurgTextExtension appExtension, DeviceContext dc) public BlurgDcExtension CreateExtension(BlurgTextExtension appExtension, DeviceContext dc)
{ {
return new BlurgGLExtension(); return new BlurgGLExtension();
} }
public void Configure(Blurg blurg)
{
blurg.EnableSystemFonts();
}
}; };