2 Commits

Author SHA1 Message Date
themixedupstuff 8bc2685206 First code commit 2024-12-13 21:57:07 +03:00
themixedupstuff fb76258e41 Initial commit of the Dashboard rewrite. 2024-12-11 20:56:39 +03:00
91 changed files with 682 additions and 9735 deletions
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
+69
View File
@@ -0,0 +1,69 @@
using System;
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; }
private DbBaseCommands() : base("DB_base",
new[]
{
BrushExtension.Instance,
})
{
AddCommand(DrawPoint = new DrawCommand<PointCommandArgs>("Point", this, PointCommandArgs.CommandSize));
}
public static readonly DbBaseCommands Instance = new DbBaseCommands();
}
public struct PointCommandArgs : IParameterSerializer<PointCommandArgs>
{
public Vector3 Position { get; private set; }
public float Size { get; private set; }
public IBrush? Brush { get; private set; }
public PointCommandArgs(Vector3 position, float size, IBrush brush)
{
Position = position;
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, 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;
Size = value.Size;
Brush = (IBrush)queue.Resources[value.BrushIndex];
}
private record struct Value(Vector3 Position, float Size, int BrushIndex);
public static readonly int CommandSize = Unsafe.SizeOf<Value>();
}
}
+88
View File
@@ -0,0 +1,88 @@
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);
}
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);
}
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 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;
}
object? IDrawCommand.GetParams(DrawQueue queue, ReadOnlySpan<byte> param)
{
return GetParams(queue, param);
}
}
}
+72
View File
@@ -0,0 +1,72 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Numerics;
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, Vector3 position, float size, IBrush brush)
{
var controller = queue.GetController(DbBaseCommands.Instance);
controller.EnsureSize(position);
controller.Write(DbBaseCommands.Instance.DrawPoint, new PointCommandArgs(position, size, brush));
}
}
}
+263
View File
@@ -0,0 +1,263 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Numerics;
namespace Dashboard.Drawing
{
public class DrawQueue : 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>
/// Size of the image in points.
/// </summary>
public Vector3 Size { get; private set; } = Vector3.Zero;
/// <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.Capacity = 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]);
}
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);
}
}
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.");
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)
{
int value = 0;
for (int i = 0; i < bytes.Length; i++)
{
byte b = bytes[i];
value = (value << 7) | b;
if ((b & (1 << 7)) == 0)
break;
}
return value;
}
public void Dispose()
{
throw new NotImplementedException();
}
private class DrawController(DrawQueue Queue) : IDrawController
{
public void EnsureSize(Vector3 size)
{
Queue.Size = Vector3.Max(Queue.Size, size);
}
public int Require(IDrawExtension extension)
{
return Queue.RequireExtension(extension);
}
public int GetResourceIndex(IDrawResource resource)
{
return Queue._resources.Intern(resource);
}
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);
}
}
private class HashList<T> : IReadOnlyList<T>
where T : notnull
{
private readonly List<T> _list = new List<T>();
private readonly Dictionary<T, int> _map = new Dictionary<T, int>();
public T this[int index] => _list[index];
public int Count => _list.Count;
public int Intern(T value)
{
if (_map.TryGetValue(value, out int index))
return index;
index = Count;
_list.Add(value);
_map.Add(value, index);
return index;
}
public void Clear()
{
_list.Clear();
_map.Clear();
}
public IEnumerator<T> GetEnumerator() => _list.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator();
}
}
public interface IDrawController
{
/// <summary>
/// Ensures that the canvas is at least a certain size.
/// </summary>
/// <param name="size">The minimum size.</param>
void EnsureSize(Vector3 size);
/// <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>;
}
}
+13
View File
@@ -0,0 +1,13 @@
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
@@ -0,0 +1,14 @@
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);
}
}
+33
View File
@@ -0,0 +1,33 @@
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 : IBrush
{
public IDrawExtension Kind { get; } = SolidBrushExtension.Instance;
public Color Color { get; }
public SolidBrush(Color color)
{
Color = color;
}
}
public class SolidBrushExtension : DrawExtension
{
private SolidBrushExtension() : base("DB_Brush_solid", new[] { BrushExtension.Instance }) { }
public static readonly SolidBrushExtension Instance = new SolidBrushExtension();
}
}
@@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LanguageVersion>7.3</LanguageVersion>
<Nullable>disable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ReFuel.FreeType" Version="0.1.0-rc.5" />
<PackageReference Include="ReFuel.StbImage" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Dashboard\Dashboard.csproj" />
</ItemGroup>
</Project>
@@ -1,13 +0,0 @@
namespace Dashboard.Media.Defaults
{
internal static class EnvironmentVariables
{
public const string SerifFont = "QUIK_SERIF_FONT";
public const string SansFont = "QUIK_SANS_FONT";
public const string MonospaceFont = "QUIK_MONOSPACE_FONT";
public const string CursiveFont = "QUIK_CURSIVE_FONT";
public const string FantasyFont = "QUIK_FANTASY_FONT";
public const string FallbackFontDatabase = "QUIK_FALLBACK_FONT_DB";
}
}
-16
View File
@@ -1,16 +0,0 @@
using System;
using ReFuel.FreeType;
namespace Dashboard.Media.Defaults
{
public static class FTProvider
{
private static FTLibrary _ft;
public static FTLibrary Ft => _ft;
static FTProvider()
{
FT.InitFreeType(out _ft);
}
}
}
@@ -1,269 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using System.Text.Json;
using ReFuel.FreeType;
using Dashboard.Media.Font;
using Dashboard.PAL;
using Dashboard.Media.Defaults.Linux;
namespace Dashboard.Media.Defaults.Fallback
{
public class FallbackFontDatabase : IFontDataBase
{
private readonly string DbPath =
Environment.GetEnvironmentVariable(EnvironmentVariables.FallbackFontDatabase) ??
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "QUIK/fontdb.json");
private Dictionary<FontFace, FileInfo> FilesMap { get; } = new Dictionary<FontFace, FileInfo>();
private Dictionary<string, List<FontFace>> ByFamily { get; } = new Dictionary<string, List<FontFace>>();
private Dictionary<SystemFontFamily, FontFace> SystemFonts { get; } = new Dictionary<SystemFontFamily, FontFace>();
private List<FontFace> All { get; } = new List<FontFace>();
IEnumerable<FontFace> IFontDataBase.All => this.All;
public FallbackFontDatabase(bool rebuild = false)
{
// Load existing database if desired.
List<DbEntry> database;
if(!rebuild)
{
database = LoadDatabase();
}
else
{
database = new List<DbEntry>();
}
VerifyDatabase(database);
FlushDatabase(database);
database.ForEach(x => AddFont(x.Face, new FileInfo(x.FilePath)));
(FontFace, FileInfo) serif = FontDataBaseProvider.ResolveSystemFont(EnvironmentVariables.SerifFont, LinuxFonts.DefaultSerifFamilies, this);
SystemFonts[SystemFontFamily.Serif] = serif.Item1;
(FontFace, FileInfo) sans = FontDataBaseProvider.ResolveSystemFont(EnvironmentVariables.SansFont, LinuxFonts.DefaultSansFamilies, this);
SystemFonts[SystemFontFamily.Sans] = sans.Item1;
(FontFace, FileInfo) mono = FontDataBaseProvider.ResolveSystemFont(EnvironmentVariables.MonospaceFont, LinuxFonts.DefaultMonospaceFamilies, this);
SystemFonts[SystemFontFamily.Monospace] = mono.Item1;
(FontFace, FileInfo) cursive = FontDataBaseProvider.ResolveSystemFont(EnvironmentVariables.CursiveFont, LinuxFonts.DefaultCursiveFamilies, this);
SystemFonts[SystemFontFamily.Cursive] = cursive.Item1;
(FontFace, FileInfo) fantasy = FontDataBaseProvider.ResolveSystemFont(EnvironmentVariables.FantasyFont, LinuxFonts.DefaultFantasyFamilies, this);
SystemFonts[SystemFontFamily.Fantasy] = fantasy.Item1;
}
public FileInfo FontFileInfo(FontFace face)
{
if (FilesMap.TryGetValue(face, out FileInfo info))
return info;
else
return null;
}
public Stream Open(FontFace face)
{
return FontFileInfo(face)?.OpenRead() ?? throw new FileNotFoundException();
}
public IEnumerable<FontFace> Search(FontFace prototype, FontMatchCriteria criteria = FontMatchCriteria.All)
{
// A bit scuffed and LINQ heavy but it should work.
IEnumerable<FontFace> candidates;
if (criteria.HasFlag(FontMatchCriteria.Family))
{
List<FontFace> siblings;
if (!ByFamily.TryGetValue(prototype.Family, out siblings))
{
return Enumerable.Empty<FontFace>();
}
candidates = siblings;
}
else
{
candidates = All;
}
return
candidates
.Where(x =>
implies(criteria.HasFlag(FontMatchCriteria.Slant), prototype.Slant == x.Slant) ||
implies(criteria.HasFlag(FontMatchCriteria.Weight), prototype.Weight == x.Weight) ||
implies(criteria.HasFlag(FontMatchCriteria.Stretch), prototype.Stretch == x.Stretch)
)
.OrderByDescending(x =>
(prototype.Slant == x.Slant ? 1 : 0) +
(prototype.Weight == x.Weight ? 1 : 0) +
(prototype.Stretch == x.Stretch ? 1 : 0) +
confidence(prototype.Family, x.Family) * 3
);
// a => b = a'+b
static bool implies(bool a, bool b)
{
return !a || b;
}
static int confidence(string target, string testee)
{
int i;
for (i = 0; i < target.Length && i < testee.Length && target[i] == testee[i]; i++);
return i;
}
}
public FontFace GetSystemFontFace(SystemFontFamily family)
{
return SystemFonts[family];
}
private void AddFont(FontFace face, FileInfo file)
{
if (!All.Contains(face))
All.Add(face);
FilesMap.TryAdd(face, file);
if (!ByFamily.TryGetValue(face.Family, out List<FontFace> siblings))
{
siblings = new List<FontFace>();
ByFamily.Add(face.Family, siblings);
}
if (!siblings.Contains(face))
siblings.Add(face);
}
private List<DbEntry> LoadDatabase()
{
FileInfo info = new FileInfo(DbPath);
if (!info.Exists)
return new List<DbEntry>();
using Stream str = info.OpenRead();
try
{
return JsonSerializer.Deserialize<List<DbEntry>>(str);
}
catch
{
return new List<DbEntry>();
}
}
private void VerifyDatabase(List<DbEntry> db)
{
// Very slow way to do this but how many fonts could a system have on average?
Dictionary<string, DbEntry> entires = new Dictionary<string, DbEntry>();
foreach (DbEntry entry in db)
{
FileInfo info = new FileInfo(entry.FilePath);
// Reprocess fonts that appear like this.
if (!info.Exists) continue;
else if (info.LastWriteTime > entry.AccessTime) continue;
}
string fontpath = null;
try
{
fontpath = Environment.GetFolderPath(Environment.SpecialFolder.Fonts);
if (string.IsNullOrEmpty(fontpath))
throw new Exception();
}
catch
{
foreach (string path in FontPaths)
{
if (Directory.Exists(path))
{
fontpath = path;
break;
}
}
// rip
if (string.IsNullOrEmpty(fontpath))
return;
}
SearchPathForFonts(entires, fontpath);
db.Clear();
db.AddRange(entires.Values);
}
private static void SearchPathForFonts(Dictionary<string, DbEntry> entries, string path)
{
DirectoryInfo dir = new DirectoryInfo(path);
foreach (FileInfo file in dir.EnumerateFiles())
{
SearchFileForFonts(entries, file);
}
foreach (DirectoryInfo directory in dir.EnumerateDirectories())
{
SearchPathForFonts(entries, directory.FullName);
}
}
private static void SearchFileForFonts(Dictionary<string, DbEntry> entries, FileInfo file)
{
if (entries.ContainsKey(file.FullName))
return;
if (FT.NewFace(FTProvider.Ft, file.FullName, 0, out FTFace face) != FTError.None)
return;
FontFace facename = FontFace.Parse(face.FamilyName, face.StyleName);
DbEntry entry = new DbEntry(facename, file.FullName);
entries.Add(file.FullName, entry);
FT.DoneFace(face);
}
private void FlushDatabase(List<DbEntry> db)
{
FileInfo info = new FileInfo(DbPath);
Directory.CreateDirectory(Path.GetDirectoryName(DbPath));
using Stream str = info.Open(FileMode.Create);
JsonSerializer.Serialize(str, db);
}
private static readonly string[] FontPaths = new string[] {
"/usr/share/fonts",
};
[JsonSerializable(typeof(DbEntry))]
private class DbEntry
{
[JsonIgnore] public FontFace Face => new FontFace(Family, Slant, Weight, Stretch);
public string Family { get; set; }
public FontSlant Slant { get; set; }
public FontWeight Weight { get; set; }
public FontStretch Stretch { get; set; }
public string FilePath { get; set; }
public DateTime AccessTime { get; set; }
public DbEntry() {}
public DbEntry(FontFace face, string path)
{
Family = face.Family;
Slant = face.Slant;
Weight = face.Weight;
Stretch = face.Stretch;
FilePath = path;
AccessTime = DateTime.Now;
}
}
}
}
@@ -1,93 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using ReFuel.FreeType;
using Dashboard.Media.Defaults.Fallback;
using Dashboard.Media.Defaults.Linux;
using Dashboard.Media.Font;
using Dashboard.PAL;
namespace Dashboard.Media.Defaults
{
public static class FontDataBaseProvider
{
public static IFontDataBase Instance { get; }
static FontDataBaseProvider()
{
try
{
// TODO: add as other operating systems are supported.
if (OperatingSystem.IsLinux())
{
Instance = new FontConfigFontDatabase();
}
else
{
Instance = new FallbackFontDatabase();
}
}
catch (Exception ex)
{
throw new NotSupportedException("Could not load a suitable font database implementation.", ex);
}
}
public static (FontFace, FileInfo) ResolveSystemFont(string envVar, string defaults, IFontDataBase db)
{
StringBuilder builder = new StringBuilder();
string user = Environment.GetEnvironmentVariable(envVar);
if (user != null)
{
builder.Append(user);
builder.Append(':');
}
builder.Append(defaults);
string[] list = builder.ToString().Split(':');
foreach (string item in list)
{
if (File.Exists(item))
{
// Process file.
if (FT.NewFace(FTProvider.Ft, item, 0, out FTFace ftface) != FTError.None)
{
continue;
}
FontFace face = FontFace.Parse(ftface.FamilyName, ftface.StyleName);
FT.DoneFace(ftface);
return (face, new FileInfo(item));
}
else
{
IEnumerable<FontFace> faces = db.Search(
new FontFace(item, FontSlant.Normal, FontWeight.Normal, FontStretch.Normal),
FontMatchCriteria.Family);
if (faces.Any())
{
FontFace face = faces.First();
return (face, db.FontFileInfo(face));
}
}
}
try
{
FontFace face = db.GetSystemFontFace(SystemFontFamily.Sans);
return (face, db.FontFileInfo(face));
}
catch (Exception ex)
{
throw new NotImplementedException("No fallback font yet.", ex);
}
}
}
}
@@ -1,24 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Dashboard.PAL;
namespace Dashboard.Media.Defaults
{
public class FreeTypeFontFactory : IFontFactory
{
public bool TryOpen(Stream stream, [NotNullWhen(true)] out QFont font)
{
try
{
font = new QFontFreeType(stream);
return true;
}
catch
{
font = null;
return false;
}
}
}
}
@@ -1,230 +0,0 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Text;
using Dashboard;
namespace Dashboard.Media.Defaults
{
public static unsafe class FontConfig
{
private const string fontconfig = "fontconfig";
public static bool Exists { get; }
public static IntPtr FAMILY { get; } = Marshal.StringToHGlobalAnsi("family");
public static IntPtr STYLE { get; } = Marshal.StringToHGlobalAnsi("style");
public static IntPtr FILE { get; } = Marshal.StringToHGlobalAnsi("file");
public static IntPtr WEIGHT { get; } = Marshal.StringToHGlobalAnsi("weight");
public static IntPtr SLANT { get; } = Marshal.StringToHGlobalAnsi("slant");
static FontConfig()
{
try
{
if (FcInitLoadConfigAndFonts() == null)
{
Exists = false;
}
Exists = true;
}
catch
{
Exists = false;
}
}
[DllImport(fontconfig, EntryPoint = "FcInitLoadConfigAndFonts")]
public static extern FcConfig* FcInitLoadConfigAndFonts();
[DllImport(fontconfig, EntryPoint = "FcConfigGetCurrent")]
public static extern FcConfig ConfigGetCurrent();
[DllImport(fontconfig, EntryPoint = "FcPatternCreate")]
public static extern FcPattern PatternCreate();
[DllImport(fontconfig, EntryPoint = "FcPatternCreate")]
public static extern bool FcPatternAdd(FcPattern pattern, IntPtr what, FcValue value, bool append);
[DllImport(fontconfig, EntryPoint = "FcObjectSetBuild", CallingConvention = CallingConvention.Cdecl)]
public static extern FcObjectSet ObjectSetBuild(IntPtr i1, IntPtr i2, IntPtr i3, IntPtr i4, IntPtr i5, IntPtr i6);
public static FcObjectSet ObjectSetBuild(IntPtr i1)
{
return ObjectSetBuild(i1, IntPtr.Zero);
}
public static FcObjectSet ObjectSetBuild(IntPtr i1, IntPtr i2)
{
return ObjectSetBuild(i1, i2, IntPtr.Zero);
}
public static FcObjectSet ObjectSetBuild(IntPtr i1, IntPtr i2, IntPtr i3)
{
return ObjectSetBuild(i1, i2, i3, IntPtr.Zero);
}
public static FcObjectSet ObjectSetBuild(IntPtr i1, IntPtr i2, IntPtr i3, IntPtr i4)
{
return ObjectSetBuild(i1, i2, i3, i4, IntPtr.Zero);
}
public static FcObjectSet ObjectSetBuild(IntPtr i1, IntPtr i2, IntPtr i3, IntPtr i4, IntPtr i5)
{
return ObjectSetBuild(i1, i2, i3, i4, i5, IntPtr.Zero);
}
[DllImport(fontconfig, EntryPoint = "FcFontList")]
public static extern FcFontSet FontList(FcConfig config, FcPattern pattern, FcObjectSet objectSet);
[DllImport(fontconfig, EntryPoint = "FcNameUnparse")]
public static extern IntPtr NameUnparse(FcPattern pat);
public static string NameUnparseStr(FcPattern pat) => Marshal.PtrToStringAnsi(NameUnparse(pat));
[DllImport(fontconfig, EntryPoint = "FcPatternGetString")]
public static extern FcResult PatternGetString(FcPattern p, IntPtr what, int n, out IntPtr val);
public static FcResult PatternGetString(FcPattern p, IntPtr what, out string str)
{
FcResult i = PatternGetString(p, what, 0, out IntPtr ptr);
if (i == FcResult.Match)
{
str = Marshal.PtrToStringAnsi(ptr);
}
else
{
str = null;
}
Marshal.FreeHGlobal(ptr);
return i;
}
[DllImport(fontconfig, EntryPoint = "FcPatternGet")]
public static extern FcResult PatternGet(FcPattern p, IntPtr what, int id, out FcValue value);
[DllImport(fontconfig, EntryPoint = "FcFontSetDestroy")]
public static extern void FontSetDestroy(FcFontSet fs);
[DllImport(fontconfig, EntryPoint = "FcObjectSetDestroy")]
public static extern void ObjectSetDestroy (FcObjectSet os);
[DllImport(fontconfig, EntryPoint = "FcConfigDestroy")]
public static extern void ConfigDestroy (FcConfig cfg);
[DllImport(fontconfig, EntryPoint = "FcPatternDestroy")]
public static extern void PatternDestroy (FcPattern os);
#region Range
[DllImport(fontconfig, EntryPoint = "FcRangeCreateDouble")]
public static extern IntPtr RangeCreateDouble(double begin, double end);
[DllImport(fontconfig, EntryPoint = "FcRangeCreateInteger")]
public static extern IntPtr RangeCreateInteger (int begin, int end);
[DllImport(fontconfig, EntryPoint = "FcRangeDestroy")]
public static extern void RangeDestroy(IntPtr range);
[DllImport(fontconfig, EntryPoint = "FcRangeCopy")]
public static extern IntPtr RangeCopy (IntPtr range);
[DllImport(fontconfig, EntryPoint = "FcRangeGetDouble")]
public static extern bool RangeGetDouble(IntPtr range, out double start, out double end);
#endregion
}
public enum FcResult
{
Match,
NoMatch,
TypeMismatch,
NoId,
OutOfMemory
}
public struct FcConfig
{
public readonly IntPtr Handle;
}
public struct FcPattern
{
public readonly IntPtr Handle;
}
public unsafe struct FcObjectSet
{
public readonly IntPtr Handle;
private Accessor* AsPtr => (Accessor*)Handle;
public int NObject => AsPtr->nobject;
public int SObject => AsPtr->sobject;
#pragma warning disable CS0649 // Will always have default value.
private struct Accessor
{
public int nobject;
public int sobject;
public byte** objects;
}
#pragma warning restore CS0649
}
public unsafe struct FcFontSet
{
public readonly IntPtr Handle;
private Accessor* AsPtr => (Accessor*)Handle;
public int NFont => AsPtr->nfont;
public int SFont => AsPtr->sfont;
public FcPattern this[int i]
{
get
{
if (i < 0 || i >= NFont)
throw new IndexOutOfRangeException();
return AsPtr->fonts[i];
}
}
#pragma warning disable CS0649 // Will always have default value.
private struct Accessor
{
public int nfont;
public int sfont;
public FcPattern* fonts;
}
#pragma warning restore CS0649
}
public enum FcType
{
Unknown = -1,
Void,
Integer,
Double,
String,
Bool,
Matrix,
CharSet,
FTFace,
LangSet,
Range
}
[StructLayout(LayoutKind.Explicit)]
public readonly struct FcValue
{
[FieldOffset(0)] public readonly FcType Type;
[FieldOffset(sizeof(FcType))] public readonly IntPtr Pointer;
[FieldOffset(sizeof(FcType))] public readonly int Int;
[FieldOffset(sizeof(FcType))] public readonly double Double;
}
}
@@ -1,168 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using ReFuel.FreeType;
using Dashboard.Media.Font;
using Dashboard.PAL;
namespace Dashboard.Media.Defaults.Linux
{
/// <summary>
/// Font database for Linux libfontconfig systems.
/// </summary>
public class FontConfigFontDatabase : IFontDataBase
{
private Dictionary<FontFace, FileInfo> FilesMap { get; } = new Dictionary<FontFace, FileInfo>();
private Dictionary<string, List<FontFace>> ByFamily { get; } = new Dictionary<string, List<FontFace>>();
private Dictionary<SystemFontFamily, FontFace> SystemFonts { get; } = new Dictionary<SystemFontFamily, FontFace>();
private List<FontFace> All { get; } = new List<FontFace>();
IEnumerable<FontFace> IFontDataBase.All => this.All;
public FontConfigFontDatabase()
{
if (!FontConfig.Exists)
{
throw new NotSupportedException("This host doesn't have fontconfig installed.");
}
FcConfig config = FontConfig.ConfigGetCurrent();
FcPattern pattern = FontConfig.PatternCreate();
FcObjectSet os = FontConfig.ObjectSetBuild(FontConfig.FAMILY, FontConfig.STYLE, FontConfig.FILE);
FcFontSet fs = FontConfig.FontList(config, pattern, os);
for (int i = 0; i < fs.NFont; i++)
{
FcPattern current = fs[i];
if (
FontConfig.PatternGetString(current, FontConfig.FAMILY, 0, out IntPtr pFamily) != FcResult.Match ||
FontConfig.PatternGetString(current, FontConfig.STYLE, 0, out IntPtr pStyle) != FcResult.Match)
{
continue;
}
string family = Marshal.PtrToStringUTF8(pFamily);
string style = Marshal.PtrToStringUTF8(pStyle);
FontFace face = FontFace.Parse(family, style);
FontConfig.PatternGetString(current, FontConfig.FILE, 0, out IntPtr pFile);
string file = Marshal.PtrToStringAnsi(pFile);
AddFont(face, new FileInfo(file));
}
FontConfig.FontSetDestroy(fs);
FontConfig.ObjectSetDestroy(os);
FontConfig.PatternDestroy(pattern);
(FontFace, FileInfo) serif = FontDataBaseProvider.ResolveSystemFont(EnvironmentVariables.SerifFont, LinuxFonts.DefaultSerifFamilies, this);
SystemFonts[SystemFontFamily.Serif] = serif.Item1;
(FontFace, FileInfo) sans = FontDataBaseProvider.ResolveSystemFont(EnvironmentVariables.SansFont, LinuxFonts.DefaultSansFamilies, this);
SystemFonts[SystemFontFamily.Sans] = sans.Item1;
(FontFace, FileInfo) mono = FontDataBaseProvider.ResolveSystemFont(EnvironmentVariables.MonospaceFont, LinuxFonts.DefaultMonospaceFamilies, this);
SystemFonts[SystemFontFamily.Monospace] = mono.Item1;
(FontFace, FileInfo) cursive = FontDataBaseProvider.ResolveSystemFont(EnvironmentVariables.CursiveFont, LinuxFonts.DefaultCursiveFamilies, this);
SystemFonts[SystemFontFamily.Cursive] = cursive.Item1;
(FontFace, FileInfo) fantasy = FontDataBaseProvider.ResolveSystemFont(EnvironmentVariables.FantasyFont, LinuxFonts.DefaultFantasyFamilies, this);
SystemFonts[SystemFontFamily.Fantasy] = fantasy.Item1;
AddFont(serif.Item1, serif.Item2);
AddFont(sans.Item1, sans.Item2);
AddFont(mono.Item1, mono.Item2);
AddFont(cursive.Item1, cursive.Item2);
AddFont(fantasy.Item1, fantasy.Item2);
}
private void AddFont(FontFace face, FileInfo file)
{
if (!All.Contains(face))
All.Add(face);
FilesMap.TryAdd(face, file);
if (!ByFamily.TryGetValue(face.Family, out List<FontFace> siblings))
{
siblings = new List<FontFace>();
ByFamily.Add(face.Family, siblings);
}
if (!siblings.Contains(face))
siblings.Add(face);
}
public IEnumerable<FontFace> Search(FontFace prototype, FontMatchCriteria criteria = FontMatchCriteria.All)
{
// A bit scuffed and LINQ heavy but it should work.
IEnumerable<FontFace> candidates;
if (criteria.HasFlag(FontMatchCriteria.Family))
{
List<FontFace> siblings;
if (!ByFamily.TryGetValue(prototype.Family, out siblings))
{
return Enumerable.Empty<FontFace>();
}
candidates = siblings;
}
else
{
candidates = All;
}
return
candidates
.Where(x =>
implies(criteria.HasFlag(FontMatchCriteria.Slant), prototype.Slant == x.Slant) ||
implies(criteria.HasFlag(FontMatchCriteria.Weight), prototype.Weight == x.Weight) ||
implies(criteria.HasFlag(FontMatchCriteria.Stretch), prototype.Stretch == x.Stretch)
)
.OrderByDescending(x =>
(prototype.Slant == x.Slant ? 1 : 0) +
(prototype.Weight == x.Weight ? 1 : 0) +
(prototype.Stretch == x.Stretch ? 1 : 0) +
confidence(prototype.Family, x.Family) * 3
);
// a => b = a'+b
static bool implies(bool a, bool b)
{
return !a || b;
}
static int confidence(string target, string testee)
{
int i;
for (i = 0; i < target.Length && i < testee.Length && target[i] == testee[i]; i++);
return i;
}
}
public FileInfo FontFileInfo(FontFace face)
{
if (FilesMap.TryGetValue(face, out FileInfo info))
return info;
else
return null;
}
public Stream Open(FontFace face)
{
return FontFileInfo(face)?.OpenRead() ?? throw new FileNotFoundException();
}
public FontFace GetSystemFontFace(SystemFontFamily family)
{
return SystemFonts[family];
}
}
}
@@ -1,11 +0,0 @@
namespace Dashboard.Media.Defaults.Linux
{
internal static class LinuxFonts
{
public const string DefaultSerifFamilies = "Noto Serif:Nimbus Roman:Liberation Serif:FreeSerif:Times:Times New Roman";
public const string DefaultSansFamilies = "Noto Sans:Nimbus Sans:Droid Sans:Liberation Sans:FreeSans:Helvetica Neue:Helvetica:Arial";
public const string DefaultMonospaceFamilies = "Noto Mono:Nimbus Mono PS:Liberation Mono:DejaVu Mono:FreeMono:Lucida Console:Consolas:Courier:Courier New";
public const string DefaultCursiveFamilies = "";
public const string DefaultFantasyFamilies = "";
}
}
-83
View File
@@ -1,83 +0,0 @@
using System;
using System.Buffers;
using System.IO;
using ReFuel.FreeType;
using Dashboard.Media.Color;
using Dashboard.Media.Font;
namespace Dashboard.Media.Defaults
{
public class QFontFreeType : QFont
{
private MemoryStream ms;
private FTFace face;
public override FontFace Face => throw new NotImplementedException();
public QFontFreeType(Stream stream)
{
ms = new MemoryStream();
stream.CopyTo(ms);
FTError e = FT.NewMemoryFace(Ft, ms.GetBuffer(), ms.Length, 0, out face);
if (e != FTError.None)
{
throw new Exception("Could not load font face from stream.");
}
}
public override bool HasRune(int rune)
{
return FT.GetCharIndex(face, (ulong)rune) != 0;
}
protected override QImage Render(out QGlyphMetrics metrics, int codepoint, float size, in FontRasterizerOptions options)
{
FT.SetCharSize(face, 0, (long)Math.Round(64*size), 0, (uint)Math.Round(options.Resolution));
uint index = FT.GetCharIndex(face, (ulong)codepoint);
FT.LoadGlyph(face, index, FTLoadFlags.Default);
ref readonly FTGlyphMetrics ftmetrics = ref face.Glyph.Metrics;
metrics = new QGlyphMetrics(codepoint,
new QVec2(ftmetrics.Width/64f, ftmetrics.Height/64f),
new QVec2(ftmetrics.HorizontalBearingX/64f, ftmetrics.HorizontalBearingY/64f),
new QVec2(ftmetrics.VerticalBearingX/64f, ftmetrics.VerticalBearingY/64f),
new QVec2(ftmetrics.HorizontalAdvance/64f, ftmetrics.VerticalAdvance/64f)
);
FT.RenderGlyph(face.Glyph, options.Sdf ? FTRenderMode.Sdf : FTRenderMode.Normal);
ref readonly FTBitmap bitmap = ref face.Glyph.Bitmap;
if (bitmap.Width == 0 || bitmap.Pitch == 0 || bitmap.Buffer == IntPtr.Zero)
{
return null;
}
QImageBuffer image = new QImageBuffer(QImageFormat.AlphaU8, (int)bitmap.Width, (int)bitmap.Rows);
image.LockBits2d(out QImageLock lk, QImageLockOptions.Default);
unsafe
{
Buffer.MemoryCopy((void*)bitmap.Buffer, (void*)lk.ImagePtr, lk.Width * lk.Height, bitmap.Width * bitmap.Rows);
}
image.UnlockBits();
return image;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
ms.Dispose();
}
FT.DoneFace(face);
}
private static FTLibrary Ft => FTProvider.Ft;
}
}
-98
View File
@@ -1,98 +0,0 @@
using System;
using System.IO;
using Dashboard.Media.Color;
using ReFuel.Stb;
namespace Dashboard.Media.Defaults
{
public unsafe class QImageStbi : QImage
{
private readonly StbImage image;
private QImageBuffer buffer;
private bool isSdf = false;
public override int Width => image.Width;
public override int Height => image.Height;
public override int Depth => 1;
public override bool IsSdf => isSdf;
public override QImageFormat InternalFormat => Stb2QImageFormat(image.Format);
public QImageStbi(Stream source)
{
// According to the stbi documentation, only a specific type of PNG
// files are premultiplied out of the box (iPhone PNG). Take the
// precision loss L and move on.
StbImage.FlipVerticallyOnLoad = true;
StbImage.UnpremultiplyOnLoad = true;
image = StbImage.Load(source);
}
public static QImageFormat Stb2QImageFormat(StbiImageFormat src)
{
switch (src)
{
case StbiImageFormat.Grey: return QImageFormat.RedU8;
case StbiImageFormat.Rgb: return QImageFormat.RgbU8;
case StbiImageFormat.Rgba: return QImageFormat.RgbaU8;
case StbiImageFormat.GreyAlpha: return QImageFormat.RaU8;
default: return QImageFormat.Undefined;
}
}
public override void LockBits2d(out QImageLock imageLock, QImageLockOptions options)
{
if (options.MipLevel > 0) throw new Exception("This image has no mip levels.");
buffer?.Dispose();
buffer = new QImageBuffer(options.Format, Width, Height);
buffer.LockBits2d(out QImageLock dst, QImageLockOptions.Default);
byte *srcPtr = (byte*)image.ImagePointer;
QImageLock src = new QImageLock(InternalFormat, Width, Height, 1, (IntPtr)srcPtr);
FormatConvert.Convert(dst, src);
if (options.Premultiply)
{
FormatConvert.Premultiply(dst);
}
imageLock = dst;
}
public override void LockBits3d(out QImageLock imageLock, QImageLockOptions options)
{
LockBits2d(out imageLock, options);
}
public override void LockBits3d(out QImageLock imageLock, QImageLockOptions options, int depth)
{
if (depth != 1) throw new ArgumentOutOfRangeException(nameof(depth));
LockBits2d(out imageLock, options);
}
public override void UnlockBits()
{
buffer.UnlockBits();
}
public void SdfHint(bool value = true)
{
isSdf = value;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
buffer?.Dispose();
image.Dispose();
}
}
}
}
-155
View File
@@ -1,155 +0,0 @@
using System;
using System.Buffers;
using System.Collections;
using System.IO;
using System.Linq;
using System.Net;
using Dashboard.Media.Font;
// WebRequest is obsolete but runs on .NET framework.
#pragma warning disable SYSLIB0014
namespace Dashboard.Media.Defaults
{
public class StbMediaLoader : MediaLoader<string>, MediaLoader<Uri>, MediaLoader<FileInfo>, MediaLoader<FontFace>
{
public bool AllowRemoteTransfers { get; set; } = false;
private readonly ArrayPool<byte> ByteArrays = ArrayPool<byte>.Create();
public IDisposable GetMedia(object key, MediaHint hint)
{
Type t = key.GetType();
/**/ if (t == typeof(string))
{
return GetMedia((string)key, hint);
}
else if (t == typeof(Uri))
{
return GetMedia((Uri)key, hint);
}
else if (t == typeof(FileInfo))
{
return GetMedia((FileInfo)key, hint);
}
else if (t == typeof(FontFace))
{
return GetMedia((FontFace)key, hint);
}
else
{
return null;
}
}
public IDisposable GetMedia(Uri uri, MediaHint hint)
{
throw new NotImplementedException();
}
public IDisposable GetMedia(string str, MediaHint hint)
{
throw new NotImplementedException();
}
public IDisposable GetMedia(FileInfo file, MediaHint hint)
{
throw new NotImplementedException();
}
public IDisposable GetMedia(FontFace key, MediaHint hint)
{
throw new NotImplementedException();
}
public Stream OpenResource(FileInfo file)
{
if (file.Exists)
{
return file.Open(FileMode.Open);
}
else
{
return null;
}
}
public Stream OpenResource(Uri uri)
{
switch (uri.Scheme)
{
case "http":
case "https":
if (!AllowRemoteTransfers) return null;
try
{
WebRequest request = HttpWebRequest.Create(uri);
WebResponse response = request.GetResponse();
MemoryStream stream = new MemoryStream();
response.GetResponseStream().CopyTo(stream);
response.Close();
stream.Position = 0;
return stream;
}
catch
{
return null;
}
case "file":
return OpenResource(new FileInfo(uri.AbsolutePath));
default:
return null;
}
}
public Stream OpenResource(string key)
{
if (File.Exists(key))
{
return File.Open(key, FileMode.Open);
}
else if (Uri.TryCreate(key, UriKind.RelativeOrAbsolute, out Uri uri))
{
return OpenResource(uri);
}
else
{
return null;
}
}
MediaHint InferMedia(Stream str, MediaHint hint)
{
if (hint != MediaHint.None)
{
return hint;
}
byte[] array = ByteArrays.Rent(4);
str.Read(array, 0, 4);
str.Position = 0;
foreach (var(type, seq) in MediaTypes)
{
if (seq.SequenceEqual(array))
return hint;
}
return MediaHint.None;
}
private readonly (MediaHint, byte[])[] MediaTypes = new (MediaHint, byte[])[] {
(MediaHint.Image, new byte[] { 0x42, 0x4d }), /* .bmp `BM` */
(MediaHint.Image, new byte[] { 0x47, 0x49, 0x46, 0x38 }), /* .gif `GIF8` */
(MediaHint.Image, new byte[] { 0xff, 0xd8, 0xff, 0xe0 }), /* .jpg (JFIF) */
(MediaHint.Image, new byte[] { 0xff, 0xd8, 0xff, 0xe1 }), /* .jpg (EXIF) */
(MediaHint.Image, new byte[] { 0x89, 0x50, 0x4e, 0x47 }), /* .png `.PNG `*/
(MediaHint.Image, new byte[] { 0x4d, 0x4d, 0x00, 0x2a }), /* .tif (motorola) */
(MediaHint.Image, new byte[] { 0x49, 0x49, 0x2a, 0x00 }), /* .tif (intel) */
(MediaHint.Font, new byte[] { 0x00, 0x01, 0x00, 0x00 }), /* .ttf */
(MediaHint.Font, new byte[] { 0x4F, 0x54, 0x54, 0x4F }), /* .otf */
};
}
}
@@ -1,151 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
#if false
namespace Quik.Media.Defaults.Win32
{
public class EnumerateFonts
{
private const byte DEFAULT_CHARSET = 1;
public static void Enumerate(FontFace font)
{
/* It's windows, just borrow the desktop window. */
IntPtr hdc = GetDC(GetDesktopWindow());
List<(LogFontA, TextMetricA)> list = new List<(LogFontA, TextMetricA)>();
LogFontA font2 = new LogFontA()
{
//FaceName = font.Family,
Weight = ((font.Style & FontStyle.Bold) != 0) ? FontWeight.Bold : FontWeight.Regular,
Italic = (font.Style & FontStyle.Italic) != 0,
CharSet = DEFAULT_CHARSET
};
Console.WriteLine(font2.FaceName);
EnumFontFamiliesExProc proc = (in LogFontA font, in TextMetricA metric, int type, IntPtr lparam) =>
{
list.Add((font, metric));
return 0;
};
EnumFontFamiliesExA(hdc, font2, proc, IntPtr.Zero, 0);
}
private const string gdi32 = "Gdi32.dll";
private const string user32 = "User32.dll";
[DllImport(gdi32)]
private static extern int EnumFontFamiliesExA(
IntPtr hdc,
in LogFontA font,
[MarshalAs(UnmanagedType.FunctionPtr)] EnumFontFamiliesExProc proc,
IntPtr lparam,
int flags /* Should be zero. */);
[DllImport(user32)]
private static extern IntPtr /* HWND */ GetDesktopWindow();
[DllImport(user32)]
private static extern IntPtr /* HDC */ GetDC(IntPtr hwnd);
private delegate int EnumFontFamiliesExProc(in LogFontA font, in TextMetricA metric, int fontType, IntPtr lParam);
private struct LogFontA
{
public long Height;
public long Width;
public long Escapement;
public long Orientation;
public FontWeight Weight;
[MarshalAs(UnmanagedType.U1)]
public bool Italic;
[MarshalAs(UnmanagedType.U1)]
public bool Underline;
[MarshalAs(UnmanagedType.U1)]
public bool StrikeOut;
public byte CharSet;
public byte OutPrecision;
public byte ClipPrecision;
public byte PitchAndFamily;
private unsafe fixed byte aFaceName[32];
public unsafe string FaceName
{
get
{
fixed (byte* str = aFaceName)
{
int len = 0;
for (; str[len] != 0 && len < 32; len++) ;
return Encoding.UTF8.GetString(str, len);
}
}
set
{
fixed (byte *str = aFaceName)
{
Span<byte> span = new Span<byte>(str, 32);
Encoding.UTF8.GetBytes(value, span);
span[31] = 0;
}
}
}
}
private struct TextMetricA
{
public long Height;
public long Ascent;
public long Descent;
public long InternalLeading;
public long ExternalLeading;
public long AveCharWidth;
public long MaxCharWidth;
public long Weight;
public long Overhang;
public long DigitizedAspectX;
public long DigitizedAspectY;
public byte FirstChar;
public byte LastChar;
public byte DefaultChar;
public byte BreakChar;
public byte Italic;
public byte Underlined;
public byte StruckOut;
public byte PitchAndFamily;
public byte CharSet;
}
private enum FontWeight : long
{
DontCare = 0,
Thin = 100,
ExtraLight = 200,
UltraLight = 200,
Light = 300,
Normal = 400,
Regular = 400,
Medium = 500,
Semibold = 600,
Demibold = 600,
Bold = 700,
Extrabold = 800,
Ultrabold = 800,
Heavy = 900,
Black = 900
}
}
}
#endif
-17
View File
@@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenTK" Version="4.8.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Dashboard\Dashboard.csproj" />
<EmbeddedResource Include="glsl\**"/>
</ItemGroup>
</Project>
-104
View File
@@ -1,104 +0,0 @@
using System;
using System.Collections.Generic;
using OpenTK.Windowing.Desktop;
using OpenTK.Windowing.GraphicsLibraryFramework;
using Dashboard.CommandMachine;
using Dashboard.Media;
using Dashboard.OpenGL;
using Dashboard.PAL;
namespace Dashboard.OpenTK
{
public class OpenTKPlatform : IDashboardPlatform
{
private readonly List<OpenTKPort> _ports = new List<OpenTKPort>();
// These shall remain a sad nop for now.
public string? Title { get; set; }
public QImage? Icon { get; set; } = null;
public event EventHandler? EventRaised;
public NativeWindowSettings DefaultSettings { get; set; } = NativeWindowSettings.Default;
public IReadOnlyList<OpenTKPort> Ports => _ports;
private bool IsGLInitialized = false;
public IDashHandle CreatePort()
{
NativeWindow window = new NativeWindow(DefaultSettings);
OpenTKPort port = new OpenTKPort(window);
_ports.Add(port);
if (!IsGLInitialized)
{
window.Context.MakeCurrent();
GL.LoadBindings(GLFW.GetProcAddress);
IsGLInitialized = true;
}
window.Closing += (ea) =>
{
Environment.Exit(0);
};
return port;
}
public void Dispose()
{
// FIXME: dispose pattern here!
// Copy the array to prevent collection modification exceptions.
foreach (OpenTKPort port in _ports.ToArray())
{
port.Dispose();
}
}
public void ProcessEvents(bool block)
{
NativeWindow.ProcessWindowEvents(block);
}
public void DestroyPort(IDashHandle port) => ((OpenTKPort)port).Dispose();
public string PortGetTitle(IDashHandle port) => ((OpenTKPort)port).Title;
public void PortSetTitle(IDashHandle port, string title) => ((OpenTKPort)port).Title = title;
public QVec2 PortGetSize(IDashHandle port) => ((OpenTKPort)port).Size;
public void PortSetSize(IDashHandle port, QVec2 size) => ((OpenTKPort)port).Size = size;
public QVec2 PortGetPosition(IDashHandle port) => ((OpenTKPort)port).Position;
public void PortSetPosition(IDashHandle port, QVec2 position) => ((OpenTKPort)port).Position = position;
public bool PortIsValid(IDashHandle port) => ((OpenTKPort)port).IsValid;
public void PortSubscribeEvent(IDashHandle port, EventHandler handler) => ((OpenTKPort)port).EventRaised += handler;
public void PortUnsubscribeEvent(IDashHandle port, EventHandler handler) => ((OpenTKPort)port).EventRaised -= handler;
public void PortFocus(IDashHandle port) => ((OpenTKPort)port).Focus();
public void PortShow(IDashHandle port, bool shown = true) => ((OpenTKPort)port).Show(shown);
public void PortPaint(IDashHandle port, CommandList commands) => ((OpenTKPort)port).Paint(commands);
public void GetMaximumImage(out int width, out int height)
{
GL.Get(GLEnum.GL_MAX_TEXTURE_SIZE, out int value);
width = height = value;
}
public void GetMaximumImage(out int width, out int height, out int depth)
{
GetMaximumImage(out width, out height);
GL.Get(GLEnum.GL_MAX_ARRAY_TEXTURE_LAYERS, out int value);
depth = value;
}
}
}
-107
View File
@@ -1,107 +0,0 @@
using System;
using OpenTK.Mathematics;
using OpenTK.Windowing.Desktop;
using Dashboard.OpenGL;
using Dashboard.CommandMachine;
using Dashboard.PAL;
using Dashboard.VertexGenerator;
namespace Dashboard.OpenTK
{
public class OpenTKPort : IDashHandle
{
private readonly NativeWindow _window;
private readonly GL21Driver _glDriver;
private readonly VertexGeneratorEngine _vertexEngine;
public string Title
{
get => _window.Title;
set => _window.Title = value;
}
public QVec2 Size
{
get
{
Vector2i size = _window.ClientSize;
return new QVec2(size.X, size.Y);
}
set
{
// OpenTK being OpenTK as usual, you can't set the client size.
Vector2i extents = _window.Size - _window.ClientSize;
Vector2i size = extents + new Vector2i((int)value.X, (int)value.Y);
_window.Size = size;
}
}
public QVec2 Position
{
get
{
Vector2i location = _window.Location;
return new QVec2(location.X, location.Y);
}
set
{
Vector2i location = new Vector2i((int)value.X, (int)value.Y);
_window.Location = location;
}
}
public bool IsValid => !isDisposed;
public event EventHandler? EventRaised;
public OpenTKPort(NativeWindow window)
{
_window = window;
_glDriver = new GL21Driver();
_vertexEngine = new VertexGeneratorEngine();
}
public void Focus()
{
_window.Focus();
}
public void Paint(CommandList queue)
{
QRectangle view = new QRectangle(Size, new QVec2(0, 0));
_vertexEngine.Reset();
_vertexEngine.ProcessCommands(view, queue);
if (!_window.Context.IsCurrent)
_window.Context.MakeCurrent();
if (!_glDriver.IsInit)
_glDriver.Init();
GL.Clear(GLEnum.GL_COLOR_BUFFER_BIT | GLEnum.GL_DEPTH_BUFFER_BIT);
_glDriver.Draw(_vertexEngine.DrawQueue, view);
_window.Context.SwapBuffers();
}
public void Show(bool shown = true)
{
_window.IsVisible = shown;
}
private bool isDisposed;
private void Dispose(bool disposing)
{
if (isDisposed) return;
if (disposing)
{
_window?.Dispose();
GC.SuppressFinalize(this);
}
isDisposed = true;
}
public void Dispose() => Dispose(true);
}
}
+23 -31
View File
@@ -3,49 +3,41 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard", "Dashboard\Dashboard.csproj", "{4FE772DD-F424-4EAC-BF88-CB8F751B4926}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dashboard", "Dashboard\Dashboard.csproj", "{49A62F46-AC1C-4240-8615-020D4FBBF964}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Media.Defaults", "Dashboard.Media.Defaults\Dashboard.Media.Defaults.csproj", "{3798F6DD-8F84-4B7D-A810-B0D4B5ACB672}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dashboard.Drawing", "Dashboard.Drawing\Dashboard.Drawing.csproj", "{1BDFEF50-C907-42C8-B63B-E4F6F585CFB5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.OpenTK", "Dashboard.OpenTK\Dashboard.OpenTK.csproj", "{2013470A-915C-46F2-BDD3-FCAA39C845EE}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{9D6CCC74-4DF3-47CB-B9B2-6BB75DF2BC40}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{40F3B724-88A1-4D4F-93AB-FE0DC07A347E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Demo", "tests\Dashboard.Demo\Dashboard.Demo.csproj", "{EAA5488E-ADF0-4D68-91F4-FAE98C8691FC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.BlurgText", "Dashboard.BlurgText\Dashboard.BlurgText.csproj", "{D05A9DEA-A5D1-43DC-AB41-36B07598B749}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dashboard.TestApplication", "tests\Dashboard.TestApplication\Dashboard.TestApplication.csproj", "{7C90B90B-DF31-439B-9080-CD805383B014}"
ProjectSection(ProjectDependencies) = postProject
{1BDFEF50-C907-42C8-B63B-E4F6F585CFB5} = {1BDFEF50-C907-42C8-B63B-E4F6F585CFB5}
{49A62F46-AC1C-4240-8615-020D4FBBF964} = {49A62F46-AC1C-4240-8615-020D4FBBF964}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{49A62F46-AC1C-4240-8615-020D4FBBF964}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{49A62F46-AC1C-4240-8615-020D4FBBF964}.Debug|Any CPU.Build.0 = Debug|Any CPU
{49A62F46-AC1C-4240-8615-020D4FBBF964}.Release|Any CPU.ActiveCfg = Release|Any CPU
{49A62F46-AC1C-4240-8615-020D4FBBF964}.Release|Any CPU.Build.0 = Release|Any CPU
{1BDFEF50-C907-42C8-B63B-E4F6F585CFB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1BDFEF50-C907-42C8-B63B-E4F6F585CFB5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1BDFEF50-C907-42C8-B63B-E4F6F585CFB5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1BDFEF50-C907-42C8-B63B-E4F6F585CFB5}.Release|Any CPU.Build.0 = Release|Any CPU
{7C90B90B-DF31-439B-9080-CD805383B014}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C90B90B-DF31-439B-9080-CD805383B014}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C90B90B-DF31-439B-9080-CD805383B014}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C90B90B-DF31-439B-9080-CD805383B014}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4FE772DD-F424-4EAC-BF88-CB8F751B4926}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4FE772DD-F424-4EAC-BF88-CB8F751B4926}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FE772DD-F424-4EAC-BF88-CB8F751B4926}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FE772DD-F424-4EAC-BF88-CB8F751B4926}.Release|Any CPU.Build.0 = Release|Any CPU
{3798F6DD-8F84-4B7D-A810-B0D4B5ACB672}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3798F6DD-8F84-4B7D-A810-B0D4B5ACB672}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3798F6DD-8F84-4B7D-A810-B0D4B5ACB672}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3798F6DD-8F84-4B7D-A810-B0D4B5ACB672}.Release|Any CPU.Build.0 = Release|Any CPU
{2013470A-915C-46F2-BDD3-FCAA39C845EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2013470A-915C-46F2-BDD3-FCAA39C845EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2013470A-915C-46F2-BDD3-FCAA39C845EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2013470A-915C-46F2-BDD3-FCAA39C845EE}.Release|Any CPU.Build.0 = Release|Any CPU
{EAA5488E-ADF0-4D68-91F4-FAE98C8691FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EAA5488E-ADF0-4D68-91F4-FAE98C8691FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EAA5488E-ADF0-4D68-91F4-FAE98C8691FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EAA5488E-ADF0-4D68-91F4-FAE98C8691FC}.Release|Any CPU.Build.0 = Release|Any CPU
{D05A9DEA-A5D1-43DC-AB41-36B07598B749}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D05A9DEA-A5D1-43DC-AB41-36B07598B749}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D05A9DEA-A5D1-43DC-AB41-36B07598B749}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D05A9DEA-A5D1-43DC-AB41-36B07598B749}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{EAA5488E-ADF0-4D68-91F4-FAE98C8691FC} = {40F3B724-88A1-4D4F-93AB-FE0DC07A347E}
{7C90B90B-DF31-439B-9080-CD805383B014} = {9D6CCC74-4DF3-47CB-B9B2-6BB75DF2BC40}
EndGlobalSection
EndGlobal
+6
View File
@@ -0,0 +1,6 @@
namespace Dashboard;
public class Class1
{
}
-62
View File
@@ -1,62 +0,0 @@
namespace Dashboard.CommandMachine
{
/// <summary>
/// Enumeration of built-in Quik commands.
/// </summary>
public enum Command
{
#region Control Commands
/// <summary>
/// Invoke a function directly.
/// </summary>
Invoke,
/// <summary>
/// Begin conditional rendering segment.
/// </summary>
ConditionalBegin,
/// <summary>
/// End conditional rendering segment.
/// </summary>
ConditionalEnd,
PushViewport,
IntersectViewport,
StoreViewport,
PopViewport,
PushZ,
IncrementZ,
AddZ,
StoreZ,
DecrementZ,
PopZ,
PushMatrix,
StoreIdentityMatrix,
StoreMatrix,
PopMatrix,
PushStyle,
StoreStyle,
PopStyle,
#endregion
#region Draw Commands
Line,
Bezier,
Rectangle,
Ellipse,
Triangle,
Polygon,
Image,
#endregion
/// <summary>
/// Start index for custom commands.
/// </summary>
CustomCommandBase = 1024,
}
}
-232
View File
@@ -1,232 +0,0 @@
using System;
using System.Collections.Generic;
namespace Dashboard.CommandMachine
{
public class CommandEngine
{
private int _zIndex = 0;
private readonly Stack<int> _zStack = new Stack<int>();
public int ZIndex => _zIndex;
private QRectangle _viewport;
private readonly Stack<QRectangle> _viewportStack = new Stack<QRectangle>();
private readonly Stack<QMat4> _matrixStack = new Stack<QMat4>();
private Command _customCommandBase = Command.CustomCommandBase;
private readonly List<QuikCommandHandler> _customCommands = new List<QuikCommandHandler>();
public QRectangle Viewport => _viewport;
public QMat4 ActiveTransforms { get; }
public StyleStack Style { get; } = new StyleStack(new Style());
protected CommandEngine()
{
Reset();
}
public Command RegisterCustomCommand(QuikCommandHandler handler)
{
Command id = _customCommandBase++;
_customCommands.Insert(id - Command.CustomCommandBase, handler);
return id;
}
public void ProcessCommands(QRectangle bounds, CommandList queue)
{
CommandQueue iterator = queue.GetEnumerator();
if (!iterator.Peek().IsCommand)
throw new ArgumentException("The first element in the iterator must be a command frame.");
Reset();
_viewport = bounds;
_viewportStack.Push(_viewport);
Frame frame;
while (iterator.TryDequeue(out frame))
{
Command cmd = (Command)frame;
switch (cmd)
{
default:
if (cmd > Command.CustomCommandBase)
{
_customCommands[cmd - Command.CustomCommandBase].Invoke(this, iterator);
}
else
{
ChildProcessCommand(cmd, iterator);
}
break;
case Command.ConditionalBegin: ConditionalHandler(iterator); break;
case Command.ConditionalEnd: /* nop */ break;
case Command.Invoke:
iterator.Dequeue().As<QuikCommandHandler>().Invoke(this, iterator);
break;
case Command.PushViewport:
_viewportStack.Push(_viewport);
break;
case Command.IntersectViewport:
_viewport = QRectangle.Intersect((QRectangle)iterator.Dequeue(), _viewport);
break;
case Command.StoreViewport:
_viewport = (QRectangle)iterator.Dequeue();
break;
case Command.PopViewport:
_viewport = _viewportStack.TryPop(out QRectangle viewport) ? viewport : bounds;
break;
case Command.PushZ:
_zStack.Push(_zIndex);
break;
case Command.IncrementZ:
_zIndex++;
break;
case Command.AddZ:
_zIndex += (int)iterator.Dequeue();
break;
case Command.StoreZ:
_zIndex = (int)iterator.Dequeue();
break;
case Command.DecrementZ:
_zIndex--;
break;
case Command.PopZ:
_zIndex = _zStack.TryPop(out int zindex) ? zindex : 0;
break;
case Command.PushStyle:
Style.Push(iterator.Dequeue().As<Style>());
break;
case Command.StoreStyle:
Style.Pop();
Style.Push(iterator.Dequeue().As<Style>());
break;
case Command.PopStyle:
Style.Pop();
break;
}
}
}
protected virtual void ChildProcessCommand(Command name, CommandQueue queue)
{
}
public virtual void Reset()
{
_zIndex = 0;
_zStack.Clear();
_viewport = new QRectangle(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue);
_viewportStack.Clear();
_matrixStack.Clear();
_matrixStack.Push(QMat4.Identity);
}
private void ConditionalHandler(CommandQueue iterator)
{
Frame frame = iterator.Dequeue();
if (
frame.IsInteger && (int)frame != 0 ||
frame.As<Func<bool>>().Invoke())
{
// Take this branch.
return;
}
// Skip this branch.
int depth = 1;
while (iterator.TryPeek(out frame))
{
if (!frame.IsCommand)
{
iterator.Dequeue();
continue;
}
switch ((Command)frame)
{
case Command.ConditionalBegin:
// Increment conditional depth.
depth++;
break;
case Command.ConditionalEnd:
// Decrement condional depth, exit if zero.
if (--depth == 0)
{
iterator.Dequeue();
return;
}
break;
}
iterator.Dequeue();
}
}
private static readonly Dictionary<Type, ICommandListSerializer> s_serializers = new Dictionary<Type, ICommandListSerializer>();
/// <summary>
/// Add a custom serializer to the command engine.
/// </summary>
/// <typeparam name="T">Type object type.</typeparam>
/// <param name="serializer">The serializer.</param>
/// <param name="overwrite">True to allow overwriting.</param>
public static void AddSerializer<T>(ICommandListSerializer serializer, bool overwrite = false)
{
if (overwrite)
{
s_serializers[typeof(T)] = serializer;
}
else
{
s_serializers.Add(typeof(T), serializer);
}
}
/// <summary>
/// Add a custom serializer to the command engine.
/// </summary>
/// <typeparam name="T">Type object type.</typeparam>
/// <param name="serializer">The serializer.</param>
/// <param name="overwrite">True to allow overwriting.</param>
public static void AddSerializer<T>(ICommandListSerializer<T> serializer, bool overwrite = false)
=> AddSerializer<T>((ICommandListSerializer)serializer, overwrite);
/// <summary>
/// Get a serializer for the given object.
/// </summary>
/// <typeparam name="T">The object type.</typeparam>
/// <param name="value">Required parameter for the C# type inference to work.</param>
/// <returns>The serializer.</returns>
public static ICommandListSerializer<T> GetSerializer<T>(ICommandListSerializable<T>? value)
where T : ICommandListSerializable<T>, new()
{
if (!s_serializers.TryGetValue(typeof(T), out var serializer))
{
serializer = new CommandListSerializableSerializer<T>();
AddSerializer<T>(serializer);
}
return (ICommandListSerializer<T>)serializer;
}
/// <summary>
/// Get a serializer for the given object.
/// </summary>
/// <typeparam name="T">The object type.</typeparam>
/// <param name="value">Required parameter for the C# type inference to work.</param>
/// <returns>The serializer.</returns>
public static ICommandListSerializer<T> GetSerializer<T>(T? value)
{
return (ICommandListSerializer<T>)s_serializers[typeof(T)];
}
}
}
@@ -1,8 +0,0 @@
namespace Dashboard.CommandMachine
{
/// <summary>
/// A delegate for a QUIK command.
/// </summary>
/// <param name="stack">The current stack.</param>
public delegate void QuikCommandHandler(CommandEngine state, CommandQueue queue);
}
-433
View File
@@ -1,433 +0,0 @@
using Dashboard.Media;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace Dashboard.CommandMachine
{
public class CommandList : IEnumerable<Frame>
{
private readonly List<Frame> _frames = new List<Frame>();
public void Clear()
{
_frames.Clear();
}
protected void Enqueue(in Frame frame)
{
_frames.Add(frame);
}
public void Invoke(QuikCommandHandler handler)
{
Enqueue(Command.Invoke);
Enqueue(new Frame(handler));
}
public void ConditionalBegin(bool value)
{
Enqueue(Command.ConditionalBegin);
Enqueue((Frame)(value ? 1 : 0));
}
public void ConditionalBegin(Func<bool> condition)
{
Enqueue(Command.ConditionalBegin);
Enqueue(new Frame(condition));
}
public void ConditionalEnd()
{
Enqueue(Command.ConditionalEnd);
}
public void PushViewport()
{
Enqueue(Command.PushViewport);
}
public void IntersectViewport(in QRectangle viewport)
{
Enqueue(Command.IntersectViewport);
Enqueue(viewport);
}
public void StoreViewport(in QRectangle viewport)
{
Enqueue(Command.StoreViewport);
Enqueue(viewport);
}
public void PopViewport()
{
Enqueue(Command.PopViewport);
}
public void PushZ()
{
Enqueue(Command.PushZ);
}
public void IncrementZ()
{
Enqueue(Command.IncrementZ);
}
public void AddZ(int value)
{
if (value == 1)
{
IncrementZ();
}
else if (value == -1)
{
DecrementZ();
}
else
{
Enqueue(Command.AddZ);
Enqueue((Frame)value);
}
}
public void StoreZ(int value)
{
Enqueue(Command.StoreZ);
Enqueue((Frame)value);
}
public void DecrementZ()
{
Enqueue(Command.DecrementZ);
}
public void PopZ()
{
Enqueue(Command.PopZ);
}
public void PushStyle(Style style)
{
Enqueue(Command.PushStyle);
Enqueue(new Frame(style));
}
public void StoreStyle(Style style)
{
Enqueue(Command.StoreStyle);
Enqueue(new Frame(style));
}
public void PopStyle()
{
Enqueue(Command.PopStyle);
}
public void Line(in QLine line)
{
Enqueue(Command.Line);
Enqueue(line);
}
public void Line(params QLine[] lines)
{
Enqueue(Command.Line);
Enqueue((Frame)lines.Length);
foreach (QLine line in lines)
Enqueue(line);
}
public void Bezier(in QBezier bezier)
{
Frame a, b;
Frame.Create(bezier, out a, out b);
Enqueue(Command.Bezier);
Enqueue(a);
Enqueue(b);
}
public void Bezier(params QBezier[] beziers)
{
Frame a, b;
Enqueue(Command.Bezier);
Enqueue((Frame)beziers.Length);
foreach (QBezier bezier in beziers)
{
Frame.Create(bezier, out a, out b);
Enqueue(a);
Enqueue(b);
}
}
public void Rectangle(in QRectangle rectangle)
{
Enqueue(Command.Rectangle);
Enqueue(rectangle);
}
public void Rectangle(QRectangle[] rectangles)
{
Enqueue(Command.Rectangle);
Enqueue((Frame)rectangles.Length);
foreach (QRectangle rectangle in rectangles)
Enqueue(rectangle);
}
public void Ellipse(in QEllipse ellipse)
{
Frame a, b;
Frame.Create(ellipse, out a, out b);
Enqueue(Command.Ellipse);
Enqueue(a);
Enqueue(b);
}
public void Ellipse(params QEllipse[] ellipses)
{
Frame a, b;
Enqueue(Command.Ellipse);
Enqueue((Frame)ellipses.Length);
foreach (QEllipse ellipse in ellipses)
{
Frame.Create(ellipse, out a, out b);
Enqueue(a);
Enqueue(b);
}
}
public void Triangle(in QTriangle triangle)
{
Enqueue(Command.Triangle);
Enqueue(triangle.A);
Enqueue(triangle.B);
Enqueue(triangle.C);
}
public void Triangle(params QTriangle[] triangles)
{
Enqueue(Command.Triangle);
Enqueue((Frame)triangles.Length);
foreach (QTriangle triangle in triangles)
{
Enqueue(triangle.A);
Enqueue(triangle.B);
Enqueue(triangle.C);
}
}
public void Polygon(params QVec2[] polygon)
{
Enqueue(Command.Polygon);
Enqueue((Frame)polygon.Length);
foreach (QVec2 vertex in polygon)
{
Enqueue(vertex);
}
}
public void Image(QImage texture, in QRectangle rectangle)
{
Enqueue(Command.Image);
Enqueue((Frame)(int)ImageCommandFlags.Single);
Enqueue(new Frame(texture));
Enqueue(rectangle);
}
public void Image(QImage texture, in QRectangle rectangle, in QRectangle uv)
{
Enqueue(Command.Image);
Enqueue((Frame)(int)(ImageCommandFlags.Single | ImageCommandFlags.UVs));
Enqueue(new Frame(texture));
Enqueue(rectangle);
Enqueue(uv);
}
public void Image(QImage texture, ReadOnlySpan<QRectangle> rectangles, bool interleavedUV = false)
{
int count = rectangles.Length;
ImageCommandFlags flags = ImageCommandFlags.None;
if (interleavedUV)
{
count /= 2;
flags |= ImageCommandFlags.UVs;
}
Enqueue(Command.Image);
Enqueue(new Frame((int)flags, count));
Enqueue(new Frame(texture));
foreach (QRectangle rectangle in rectangles)
{
Enqueue(rectangle);
}
}
public void Image(QImage texture, ReadOnlySpan<QRectangle> rectangles, ReadOnlySpan<QRectangle> uvs)
{
int count = Math.Min(rectangles.Length, uvs.Length);
Enqueue(Command.Image);
Enqueue(new Frame((int)ImageCommandFlags.UVs, count));
Enqueue(new Frame(texture));
for (int i = 0; i < count; i++)
{
Enqueue(rectangles[i]);
Enqueue(uvs[i]);
}
}
public void Image3D(QImage texture, in Image3DCall call)
{
Enqueue(Command.Image);
Enqueue(new Frame(ImageCommandFlags.Image3d | ImageCommandFlags.Single));
Enqueue(new Frame(texture));
Enqueue(call.Rectangle);
Enqueue(call.UVs);
Enqueue(new Frame(call.Layer));
}
public void Image3D(QImage texture, ReadOnlySpan<Image3DCall> calls)
{
Enqueue(Command.Image);
Enqueue(new Frame((int)ImageCommandFlags.Image3d, calls.Length));
Enqueue(new Frame(texture));
foreach (Image3DCall call in calls)
{
Enqueue(call.Rectangle);
Enqueue(call.UVs);
Enqueue(new Frame(call.Layer));
}
}
public void Splice(CommandList list)
{
foreach (Frame frame in list)
{
Enqueue(frame);
}
}
/// <summary>
/// Serialize an object into the command list.
/// </summary>
/// <typeparam name="T">The type of the value to serialize.</typeparam>
/// <param name="value">What to write into the command list.</param>
public void Write<T>(T value)
{
CommandEngine.GetSerializer(value).Serialize(value, this);
}
/// <summary>
/// Serialize an object into the command list.
/// </summary>
/// <typeparam name="T">The type of the value to serialize.</typeparam>
/// <param name="value">What to write into the command list.</param>
public void Write<T>(ICommandListSerializable<T> value)
where T : ICommandListSerializable<T>, new()
{
CommandEngine.GetSerializer(value).Serialize((T)value, this);
}
public CommandQueue GetEnumerator() => new CommandQueue(_frames);
IEnumerator<Frame> IEnumerable<Frame>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
public class CommandQueue : IEnumerator<Frame>
{
private readonly IReadOnlyList<Frame> _frames;
private int _current;
public Frame Current => _frames[_current];
object IEnumerator.Current => Current;
public CommandQueue(IReadOnlyList<Frame> frames)
{
_current = -1;
_frames = frames;
}
public void Dispose()
{
}
public bool TryDequeue([NotNullWhen(true)] out Frame frame)
{
if (MoveNext())
{
frame = Current;
return true;
}
else
{
frame = default;
return false;
}
}
public Frame Dequeue() => TryDequeue(out Frame frame) ? frame : throw new Exception("No more frames left.");
public bool TryPeek([NotNullWhen(true)] out Frame frame)
{
if (_current + 1 < _frames.Count)
{
frame = _frames[_current + 1];
return true;
}
else
{
frame = default;
return false;
}
}
public Frame Peek() => TryPeek(out Frame frame) ? frame : throw new Exception("No more frames left.");
/// <summary>
/// Deserialize an object from the command queue.
/// </summary>
/// <typeparam name="T">Type of the object to deserialize.</typeparam>
/// <param name="value">The deserialized value.</param>
public void Read<T>([NotNull] out T? value)
{
value = CommandEngine.GetSerializer(default(T)).Deserialize(this);
}
/// <summary>
/// Deserialize an object from the command queue.
/// </summary>
/// <typeparam name="T">Type of the object to deserialize.</typeparam>
/// <param name="value">The deserialized value.</param>
public void Read<T>([NotNull] out ICommandListSerializable<T>? value)
where T : ICommandListSerializable<T>, new()
{
value = CommandEngine.GetSerializer(value = null).Deserialize(this);
}
/// <inheritdoc/>
public bool MoveNext()
{
if (_current + 1 < _frames.Count)
{
_current++;
return true;
}
return false;
}
/// <inheritdoc/>
public void Reset()
{
_current = -1;
}
}
}
-356
View File
@@ -1,356 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace Dashboard.CommandMachine
{
[StructLayout(LayoutKind.Explicit)]
public struct Frame
{
[FieldOffset(0)]
private FrameType _type;
[FieldOffset(sizeof(FrameType) + 0 * sizeof(int))]
private int _i1;
[FieldOffset(sizeof(FrameType) + 1 * sizeof(int))]
private int _i2;
[FieldOffset(sizeof(FrameType) + 2 * sizeof(int))]
private int _i3;
[FieldOffset(sizeof(FrameType) + 3 * sizeof(int))]
private int _i4;
[FieldOffset(sizeof(FrameType) + 0 * sizeof(float))]
private float _f1;
[FieldOffset(sizeof(FrameType) + 1 * sizeof(float))]
private float _f2;
[FieldOffset(sizeof(FrameType) + 2 * sizeof(float))]
private float _f3;
[FieldOffset(sizeof(FrameType) + 3 * sizeof(float))]
private float _f4;
[FieldOffset(24)]
private object? _object = null;
public bool IsCommand => _type == FrameType.Command;
public bool IsInteger =>
_type == FrameType.IVec1 ||
_type == FrameType.IVec2 ||
_type == FrameType.IVec3 ||
_type == FrameType.IVec4;
public bool IsFloat =>
_type == FrameType.Vec1 ||
_type == FrameType.Vec2 ||
_type == FrameType.Vec3 ||
_type == FrameType.Vec4;
public int VectorSize
{
get
{
switch (_type)
{
case FrameType.None:
return 0;
default:
return 1;
case FrameType.Vec2: case FrameType.IVec2:
return 2;
case FrameType.Vec3: case FrameType.IVec3:
return 3;
case FrameType.Vec4: case FrameType.IVec4:
return 4;
}
}
}
public FrameType Type => _type;
public int I1 => _i1;
public int I2 => _i2;
public int I3 => _i3;
public int I4 => _i4;
public float F1 => _f1;
public float F2 => _f2;
public float F3 => _f3;
public float F4 => _f4;
public static Frame None { get; } = new Frame() {
_type = FrameType.None
};
#region Constructors
public Frame(Command command) : this()
{
_type = FrameType.Command;
_i1 = (int)command;
}
public Frame(object o)
{
_type = FrameType.Object;
_i1 = _i2 = _i3 = _i4 = default;
_f1 = _f2 = _f3 = _f4 = default;
_object = null;
_object = o;
}
public Frame(int i1)
{
_type = FrameType.IVec1;
_i1 = _i2 = _i3 = _i4 = default;
_f1 = _f2 = _f3 = _f4 = default;
_object = null;
_i1 = i1;
}
public Frame(int i1, int i2)
{
_type = FrameType.IVec2;
_i1 = _i2 = _i3 = _i4 = default;
_f1 = _f2 = _f3 = _f4 = default;
_object = null;
_i1 = i1;
_i2 = i2;
}
public Frame(int i1, int i2, int i3)
{
_type = FrameType.IVec3;
_i1 = _i2 = _i3 = _i4 = default;
_f1 = _f2 = _f3 = _f4 = default;
_object = null;
_i1 = i1;
_i2 = i2;
_i3 = i3;
}
public Frame(int i1, int i2, int i3, int i4)
{
_type = FrameType.IVec4;
_i1 = _i2 = _i3 = _i4 = default;
_f1 = _f2 = _f3 = _f4 = default;
_object = null;
_i1 = i1;
_i2 = i2;
_i3 = i3;
_i4 = i4;
}
public Frame(float f1)
{
_type = FrameType.Vec1;
_i1 = _i2 = _i3 = _i4 = default;
_f1 = _f2 = _f3 = _f4 = default;
_object = null;
_f1 = f1;
}
public Frame(float f1, float f2)
{
_type = FrameType.Vec2;
_i1 = _i2 = _i3 = _i4 = default;
_f1 = _f2 = _f3 = _f4 = default;
_object = null;
_f1 = f1;
_f2 = f2;
}
public Frame(float f1, float f2, float f3)
{
_type = FrameType.Vec3;
_i1 = _i2 = _i3 = _i4 = default;
_f1 = _f2 = _f3 = _f4 = default;
_object = null;
_f1 = f1;
_f2 = f2;
_f3 = f3;
}
public Frame(float f1, float f2, float f3, float f4)
{
_type = FrameType.Vec4;
_i1 = _i2 = _i3 = _i4 = default;
_f1 = _f2 = _f3 = _f4 = default;
_object = null;
_f1 = f1;
_f2 = f2;
_f3 = f3;
_f4 = f4;
}
#endregion
public T As<T>()
{
return (T)_object!;
}
public float GetF(int i)
{
switch (i)
{
case 0: return _f1;
case 1: return _f2;
case 2: return _f3;
case 3: return _f4;
default:
throw new ArgumentOutOfRangeException();
}
}
public int GetI(int i)
{
switch (i)
{
case 0: return _i1;
case 1: return _i2;
case 2: return _i3;
case 3: return _i4;
default:
throw new ArgumentOutOfRangeException();
}
}
#region Frame->T Conversion
public static explicit operator int(in Frame frame)
{
switch (frame.Type)
{
default:
throw new InvalidCastException();
case FrameType.Command:
case FrameType.IVec1:
case FrameType.IVec2:
case FrameType.IVec3:
case FrameType.IVec4:
return frame._i1;
case FrameType.Vec1:
case FrameType.Vec2:
case FrameType.Vec3:
case FrameType.Vec4:
return (int)frame._f1;
}
}
public static explicit operator float(in Frame frame)
{
switch (frame.Type)
{
default:
throw new InvalidCastException();
case FrameType.IVec1:
case FrameType.IVec2:
case FrameType.IVec3:
case FrameType.IVec4:
return frame._i1;
case FrameType.Vec1:
case FrameType.Vec2:
case FrameType.Vec3:
case FrameType.Vec4:
return frame._f1;
}
}
public static explicit operator Command(in Frame frame)
{
if (frame.Type != FrameType.Command)
{
throw new InvalidCastException("Not a command frame.");
}
return (Command)frame._i1;
}
public static explicit operator QVec2(in Frame frame)
{
switch (frame.Type)
{
default:
throw new InvalidCastException();
case FrameType.IVec2:
case FrameType.IVec3:
case FrameType.IVec4:
return new QVec2(frame._i1, frame._i2);
case FrameType.Vec2:
case FrameType.Vec3:
case FrameType.Vec4:
return new QVec2(frame._f1, frame._f2);
}
}
public static explicit operator QColor(in Frame frame)
{
if (frame.Type != FrameType.IVec4)
throw new InvalidCastException();
return new QColor((byte)frame._i1, (byte)frame._i2, (byte)frame._i3, (byte)frame._i4);
}
public static explicit operator QRectangle(in Frame frame)
{
switch (frame.Type)
{
default:
throw new InvalidCastException();
case FrameType.IVec4:
return new QRectangle(frame._i1, frame._i2, frame._i3, frame._i4);
case FrameType.Vec4:
return new QRectangle(frame._f1, frame._f2, frame._f3, frame._f4);
}
}
public static explicit operator QLine(in Frame frame)
{
switch (frame.Type)
{
default:
throw new InvalidCastException();
case FrameType.IVec4:
return new QLine(frame._i1, frame._i2, frame._i3, frame._i4);
case FrameType.Vec4:
return new QLine(frame._f1, frame._f2, frame._f3, frame._f4);
}
}
#endregion
public static explicit operator Frame(int i) => new Frame(i);
public static explicit operator Frame(float f) => new Frame(f);
public static implicit operator Frame(Command cmd) => new Frame(cmd);
public static implicit operator Frame(in QVec2 vector) => new Frame(vector.X, vector.Y);
public static implicit operator Frame(in QColor color) => new Frame(color.R, color.G, color.B, color.A);
public static implicit operator Frame(in QRectangle rect) => new Frame(rect.Max.X, rect.Max.Y, rect.Min.X, rect.Min.Y);
public static implicit operator Frame(in QLine line) => new Frame(line.Start.X, line.Start.Y, line.End.X, line.Start.Y);
public static void Create(in QBezier bezier, out Frame a, out Frame b)
{
a = new Frame(bezier.Start.X, bezier.Start.Y, bezier.End.X, bezier.End.Y);
b = new Frame(bezier.ControlA.X, bezier.ControlA.Y, bezier.ControlB.X, bezier.ControlB.Y);
}
public static void Create(in QEllipse ellipse, out Frame a, out Frame b)
{
a = new Frame(ellipse.Center.X, ellipse.Center.Y);
b = new Frame(ellipse.AxisA.X, ellipse.AxisA.Y, ellipse.AxisB.X, ellipse.AxisB.Y);
}
}
}
-68
View File
@@ -1,68 +0,0 @@
namespace Dashboard.CommandMachine
{
/// <summary>
/// Enumeration of command types in the Dashboard command lists.
/// </summary>
public enum FrameType
{
/// <summary>
/// A null value.
/// </summary>
None,
/// <summary>
/// A command frame.
/// </summary>
Command,
/// <summary>
/// An integer frame.
/// </summary>
IVec1,
/// <summary>
/// A two dimensional integer vector frame.
/// </summary>
IVec2,
/// <summary>
/// A three dimensional integer vector frame.
/// </summary>
IVec3,
/// <summary>
/// A four dimensional integer vector frame.
/// </summary>
IVec4,
/// <summary>
/// A floating point frame.
/// </summary>
Vec1,
/// <summary>
/// A two dimensional floating point vector frame.
/// </summary>
Vec2,
/// <summary>
/// A three dimensional floating point vector frame.
/// </summary>
Vec3,
/// <summary>
/// A four dimensional floating point vector frame.
/// </summary>
Vec4,
/// <summary>
/// A serialized object frame.
/// </summary>
Serialized,
/// <summary>
/// A .Net object frame.
/// </summary>
Object,
}
}
-18
View File
@@ -1,18 +0,0 @@
namespace Dashboard.CommandMachine
{
public enum ImageCommandFlags
{
None = 0,
Single = 1 << 0,
UVs = 1 << 1,
Image3d = 1 << 2,
}
public struct Image3DCall
{
public QRectangle Rectangle;
public QRectangle UVs;
public int Layer;
}
}
-69
View File
@@ -1,69 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using System.Threading;
namespace Dashboard.CommandMachine
{
public interface ICommandListSerializable { }
/// <summary>
/// Interface for objects that can be serialized into the Dashboard command stream.
/// </summary>
public interface ICommandListSerializable<T> : ICommandListSerializable
{
/// <summary>
/// Seralize object.
/// </summary>
/// <param name="list">The object to serialize into.</param>
void Serialize(CommandList list);
/// <summary>
/// Deserialize object.
/// </summary>
/// <param name="queue">The command queue to deserialize from.</param>
void Deserialize(CommandQueue queue);
}
/// <summary>
/// Base interface for all Command List serializers.
/// </summary>
public interface ICommandListSerializer { }
public interface ICommandListSerializer<T> : ICommandListSerializer
{
/// <summary>
/// Serialize an object into the command list.
/// </summary>
/// <param name="value">The object to serialize.</param>
/// <param name="list">The command list to serialize into.</param>
void Serialize(T value, CommandList list);
/// <summary>
/// Deserialize an object from the command queue.
/// </summary>
/// <param name="queue">The command queue.</param>
/// <returns>The object deserialized from the command queue.</returns>
[return: NotNull]
T Deserialize(CommandQueue queue);
}
/// <summary>
/// Class for automatic serialization of <see cref="ICommandListSerializable"/> objects.
/// </summary>
/// <typeparam name="T">The object type to convert.</typeparam>
internal class CommandListSerializableSerializer<T> : ICommandListSerializer<T>
where T : ICommandListSerializable<T>, new()
{
public T Deserialize(CommandQueue queue)
{
T value = new T();
value.Deserialize(queue);
return value;
}
public void Serialize(T value, CommandList list)
{
value.Serialize(list);
}
}
}
-56
View File
@@ -1,56 +0,0 @@
using System;
namespace Dashboard.Controls
{
public enum Dock
{
None,
Top,
Left,
Bottom,
Right,
Center
}
[Flags]
public enum Anchor
{
None = 0,
Top = 1 << 0,
Left = 1 << 1,
Bottom = 1 << 2,
Right = 1 << 3,
All = Top | Left | Bottom | Right
}
public enum Direction
{
Vertical,
Horizontal
}
public enum TextAlignment
{
Left,
Center,
Right,
Justify,
}
public enum VerticalAlignment
{
Top,
Center,
Bottom,
Justify,
}
public enum HorizontalAlignment
{
Left,
Center,
Right,
Justify
}
}
-50
View File
@@ -1,50 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Dashboard.Controls
{
public abstract class ContainerControl : Control, ICollection<Control>
{
private readonly List<Control> children = new List<Control>();
public int Count => children.Count;
public bool IsReadOnly => false;
public void Add(Control item)
{
children.Add(item);
}
public void Clear()
{
children.Clear();
}
public bool Contains(Control item)
{
return children.Contains(item);
}
public void CopyTo(Control[] array, int arrayIndex)
{
children.CopyTo(array, arrayIndex);
}
public IEnumerator<Control> GetEnumerator()
{
return children.GetEnumerator();
}
public bool Remove(Control item)
{
return children.Remove(item);
}
IEnumerator IEnumerable.GetEnumerator()
{
return children.GetEnumerator();
}
}
}
-125
View File
@@ -1,125 +0,0 @@
using System;
using System.Collections.Generic;
using Dashboard.CommandMachine;
namespace Dashboard.Controls
{
public abstract class Control : UIBase
{
private readonly CommandList drawCommands = new CommandList();
public Style Style { get; set; } = new Style();
public float Padding
{
get => (float)(Style["padding"] ?? 0.0f);
set => Style["padding"] = value;
}
public bool IsVisualsValid { get; private set; } = false;
public bool IsLayoutValid { get; private set; } = false;
protected bool IsLayoutSuspended { get; private set; } = false;
public void InvalidateVisual()
{
IsVisualsValid = false;
OnVisualsInvalidated(this, EventArgs.Empty);
}
public void InvalidateLayout()
{
IsLayoutValid = false;
OnLayoutInvalidated(this, EventArgs.Empty);
}
public void SuspendLayout()
{
IsLayoutSuspended = true;
}
public void ResumeLayout()
{
IsLayoutSuspended = false;
InvalidateLayout();
}
protected abstract void ValidateVisual(CommandList cmd);
protected abstract void ValidateLayout();
protected override void PaintBegin(CommandList cmd)
{
base.PaintBegin(cmd);
if (!IsLayoutValid && !IsLayoutSuspended)
{
ValidateLayout();
OnLayoutValidated(this, EventArgs.Empty);
IsLayoutValid = true;
InvalidateVisual();
}
if (!IsVisualsValid)
{
ValidateVisual(drawCommands);
OnVisualsValidated(this, EventArgs.Empty);
IsVisualsValid = true;
}
cmd.PushStyle(Style);
cmd.PushViewport();
cmd.StoreViewport(AbsoluteBounds);
cmd.Splice(drawCommands);
cmd.PopViewport();
cmd.PopStyle();
}
public event EventHandler? StyleChanged;
public event EventHandler? VisualsInvalidated;
public event EventHandler? VisualsValidated;
public event EventHandler? LayoutInvalidated;
public event EventHandler? LayoutValidated;
protected virtual void OnStyleChanged(object sender, EventArgs ea)
{
StyleChanged?.Invoke(sender, ea);
InvalidateLayout();
}
protected virtual void OnVisualsInvalidated(object sender, EventArgs ea)
{
VisualsInvalidated?.Invoke(sender, ea);
}
protected virtual void OnVisualsValidated(object sender, EventArgs ea)
{
VisualsValidated?.Invoke(sender, ea);
}
protected virtual void OnLayoutInvalidated(object sender, EventArgs ea)
{
LayoutInvalidated?.Invoke(sender, ea);
}
protected virtual void OnLayoutValidated(object sender, EventArgs ea)
{
LayoutValidated?.Invoke(sender, ea);
}
protected void ValidateChildrenLayout()
{
if (this is IEnumerable<Control> enumerable)
{
foreach (Control child in enumerable)
{
if (child.IsLayoutValid)
continue;
child.ValidateLayout();
}
}
}
}
}
-100
View File
@@ -1,100 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Dashboard.CommandMachine;
namespace Dashboard.Controls
{
public class FlowBox : ContainerControl
{
public Direction FlowDirection { get; set; }
public bool AllowWrap { get; set; }
public VerticalAlignment VerticalAlignment { get; set; }
public HorizontalAlignment HorizontalAlignment { get; set; }
public float ItemPadding { get; set; } = 4f;
protected override void ValidateLayout()
{
ValidateChildrenLayout();
}
protected override void ValidateVisual(CommandList cmd)
{
throw new NotImplementedException();
}
private void FlowHorizontal()
{
IEnumerator<Control> controls = this.GetEnumerator();
List<Control> row;
}
// Enumerate a row.
private bool EnumerateRows(IEnumerator<Control> iterator, List<Control> row)
{
float width = 0;
do
{
if (width + iterator.Current.Size.X < Size.X)
{
row.Add(iterator.Current);
width += iterator.Current.Size.X + ItemPadding;
}
else
{
return true;
}
} while (iterator.MoveNext());
return false;
}
// Flows a row of children.
private void FlowRow(List<Control> line, QVec2 offset, QVec2 size, float packedWidth)
{
QVec2 pointer = offset;
pointer.X += hstart();
foreach (Control child in line)
{
child.Position = pointer;
pointer += new QVec2(child.Size.X + hoffset(child), voffset(child));
}
float hstart()
{
return HorizontalAlignment switch {
HorizontalAlignment.Center => (size.Y - packedWidth) / 2,
HorizontalAlignment.Right => size.Y - packedWidth,
_ => 0f
};
}
float hoffset(Control child)
{
if (line.Count == 1)
return 0;
else if (HorizontalAlignment == HorizontalAlignment.Justify)
{
return ItemPadding + ((size.Y - packedWidth) / (line.Count - 1));
}
else
{
return ItemPadding;
}
}
float voffset(Control child)
{
return VerticalAlignment switch {
VerticalAlignment.Top => 0f,
VerticalAlignment.Bottom => size.Y - child.Size.Y,
_ => (size.Y - child.Size.Y) / 2,
};
}
}
}
}
-31
View File
@@ -1,31 +0,0 @@
using Dashboard.CommandMachine;
using Dashboard.Media;
using Dashboard.Typography;
namespace Dashboard.Controls
{
public class Label : Control
{
public string Text { get; set; } = string.Empty;
public QFont? Font { get; set; }
public float TextSize { get; set; }
public bool AutoSize { get; set; } = true;
protected override void ValidateLayout()
{
if (AutoSize)
{
QVec2 size = Typesetter.MeasureHorizontal(Text, TextSize, Font!);
Size = size;
}
}
protected override void ValidateVisual(CommandList cmd)
{
float padding = Padding;
QVec2 origin = new QVec2(padding, padding);
cmd.TypesetHorizontalDirect(Text, origin, TextSize, Font!);
}
}
}
-102
View File
@@ -1,102 +0,0 @@
using System;
using Dashboard.CommandMachine;
namespace Dashboard.Controls
{
/// <summary>
/// Bases for all UI elements.
/// </summary>
public abstract class UIBase
{
private QVec2 size;
public UIBase? Parent { get; protected set; }
public string? Id { get; set; }
public QRectangle Bounds
{
get => new QRectangle(Position + Size, Position);
set
{
Size = value.Size;
Position = value.Min;
}
}
public QVec2 Position { get; set; }
public QVec2 Size
{
get => size;
set
{
QVec2 oldSize = size;
size = value;
OnResized(this, new ResizedEventArgs(size, oldSize));
}
}
public QRectangle AbsoluteBounds
{
get
{
if (Parent == null)
{
return Bounds;
}
else
{
return new QRectangle(Bounds.Max + Parent.Position, Bounds.Min + Parent.Position);
}
}
}
public QVec2 MaximumSize { get; set; } = new QVec2(-1, -1);
public QVec2 MinimumSize { get; set; } = new QVec2(-1, -1);
public bool IsMaximumSizeSet => MaximumSize != new QVec2(-1, -1);
public bool IsMinimumSizeSet => MinimumSize != new QVec2(-1, -1);
public virtual void NotifyEvent(object? sender, EventArgs args)
{
}
protected virtual void PaintBegin(CommandList cmd)
{
cmd.PushViewport();
cmd.StoreViewport(AbsoluteBounds);
cmd.PushZ();
}
protected virtual void PaintEnd(CommandList cmd)
{
cmd.PopViewport();
}
public void Paint(CommandList cmd)
{
PaintBegin(cmd);
PaintEnd(cmd);
}
public event EventHandler<ResizedEventArgs>? Resized;
public virtual void OnResized(object sender, ResizedEventArgs ea)
{
Resized?.Invoke(sender, ea);
}
}
public class ResizedEventArgs : EventArgs
{
public QVec2 NewSize { get; }
public QVec2 OldSize { get; }
public ResizedEventArgs(QVec2 newSize, QVec2 oldSize)
{
NewSize = newSize;
OldSize = oldSize;
}
}
}
-8
View File
@@ -1,8 +0,0 @@
using System;
namespace Dashboard.Controls
{
public class View : UIBase
{
}
}
+3 -8
View File
@@ -1,14 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="res/**" />
</ItemGroup>
</Project>
-109
View File
@@ -1,109 +0,0 @@
using System;
namespace Dashboard.Media.Color
{
public static class FormatConvert
{
public static void Premultiply(QImageLock image)
{
switch (image.Format)
{
case QImageFormat.RaF:
case QImageFormat.RaU8:
case QImageFormat.RgbF:
case QImageFormat.RgbU8:
case QImageFormat.RgbaF:
case QImageFormat.RgbaU8:
break;
default:
return;
}
int count = image.Width * image.Height * image.Depth;
if (image.Format.IsFloat())
{
LockIOF io = new LockIOF(image);
for (int i = 0; i < count; i++)
{
QColorF color = io[i];
color.R *= color.A;
color.G *= color.A;
color.G *= color.A;
io[i] = color;
}
}
else
{
LockIO io = new LockIO(image);
for (int i = 0; i < count; i++)
{
QColor color = io[i];
float a = color.A/255.0f;
color.R = (byte)(color.R * a);
color.G = (byte)(color.G * a);
color.B = (byte)(color.B * a);
io[i] = color;
}
}
}
public static void Convert(QImageLock dst, QImageLock src)
{
if (dst.Format.IsU8() && src.Format.IsU8())
{
LockIO dstIO = new LockIO(dst);
LockIO srcIO = new LockIO(src);
int count = dst.Width * dst.Height * dst.Depth;
for (int i = 0; i < count; i++)
{
dstIO[i] = srcIO[i];
}
}
else if (dst.Format.IsU8() && src.Format.IsFloat())
{
LockIO dstIO = new LockIO(dst);
LockIOF srcIO = new LockIOF(src);
int count = dst.Width * dst.Height * dst.Depth;
for (int i = 0; i < count; i++)
{
dstIO[i] = (QColor)srcIO[i];
}
}
else if (dst.Format.IsFloat() && src.Format.IsU8())
{
LockIOF dstIO = new LockIOF(dst);
LockIO srcIO = new LockIO(src);
int count = dst.Width * dst.Height * dst.Depth;
for (int i = 0; i < count; i++)
{
dstIO[i] = (QColorF)srcIO[i];
}
}
else if (dst.Format.IsFloat() && src.Format.IsFloat())
{
LockIOF dstIO = new LockIOF(dst);
LockIOF srcIO = new LockIOF(src);
int count = dst.Width * dst.Height * dst.Depth;
for (int i = 0; i < count; i++)
{
dstIO[i] = srcIO[i];
}
}
else
{
throw new Exception("Congratulations you have broken image formats!");
}
}
}
}
-69
View File
@@ -1,69 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace Dashboard.Media.Color
{
public class QImageBuffer : QImage
{
private byte[] buffer;
GCHandle handle;
private bool isSdf = false;
public override QImageFormat InternalFormat { get; }
public override int Width { get; }
public override int Height { get; }
public override int Depth { get; }
public override bool IsSdf => isSdf;
public QImageBuffer(QImageFormat format, int width, int height, int depth = 1)
{
InternalFormat = format;
Width = width;
Height = height;
Depth = depth;
buffer = new byte[width * height * depth];
}
~QImageBuffer()
{
Dispose(false);
}
private QImageLock Lock()
{
if (handle.IsAllocated) handle.Free();
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
return new QImageLock(InternalFormat, Width, Height, Depth, ptr);
}
protected override void Dispose(bool disposing)
{
if (handle.IsAllocated) handle.Free();
GC.SuppressFinalize(this);
}
public override void LockBits2d(out QImageLock imageLock, QImageLockOptions options)
{
imageLock = Lock();
}
public override void LockBits3d(out QImageLock imageLock, QImageLockOptions options)
{
imageLock = Lock();
}
public override void LockBits3d(out QImageLock imageLock, QImageLockOptions options, int depth)
{
imageLock = Lock();
}
public override void UnlockBits()
{
handle.Free();
}
public void SetSdf(bool value = true) => isSdf = value;
}
}
-150
View File
@@ -1,150 +0,0 @@
using System;
namespace Dashboard.Media.Color
{
public unsafe struct LockIO
{
public QImageLock Lock { get; }
public int Width => Lock.Width;
public int Height => Lock.Height;
public int Depth => Depth;
public QImageFormat Format => Lock.Format;
public LockIO(QImageLock imageLock)
{
if (!imageLock.Format.IsU8())
throw new Exception("Can only read/write U8 format images");
Lock = imageLock;
}
public QColor this[int index]
{
get
{
int chan = Format.Channels();
byte *ptr = (byte*)Lock.ImagePtr + chan * index;
switch (Format)
{
default:
case QImageFormat.RedU8: return new QColor(ptr[0], 0, 0, 255);
case QImageFormat.AlphaU8: return new QColor(0, 0, 0, ptr[0]);
case QImageFormat.RaU8: return new QColor(ptr[0], 0, 0, ptr[1]);
case QImageFormat.RgbU8: return new QColor(ptr[0], ptr[1], ptr[2], 255);
case QImageFormat.RgbaU8: return new QColor(ptr[0], ptr[1], ptr[2], ptr[3]);
}
}
set
{
int chan = Format.Channels();
byte *ptr = (byte*)Lock.ImagePtr + chan * index;
switch (Format)
{
default:
case QImageFormat.RedU8:
ptr[0] = value.R;
break;
case QImageFormat.AlphaU8:
ptr[0] = value.A;
break;
case QImageFormat.RaU8:
ptr[0] = value.R;
ptr[1] = value.A;
break;
case QImageFormat.RgbU8:
ptr[0] = value.R;
ptr[1] = value.G;
ptr[2] = value.B;
break;
case QImageFormat.RgbaU8:
ptr[0] = value.R;
ptr[1] = value.G;
ptr[2] = value.B;
ptr[3] = value.A;
break;
}
}
}
public QColor this[int x, int y, int z = 0]
{
get => this[x + y * Width + z * Width * Height];
set => this[x + y * Width + z * Width * Height] = value;
}
}
public unsafe struct LockIOF
{
public QImageLock Lock { get; }
public int Width => Lock.Width;
public int Height => Lock.Height;
public int Depth => Depth;
public QImageFormat Format => Lock.Format;
public LockIOF(QImageLock imageLock)
{
if (!imageLock.Format.IsFloat())
throw new Exception("Can only read/write U8 format images");
Lock = imageLock;
}
public QColorF this[int index]
{
get
{
int chan = Format.Channels();
float *ptr = (float*)Lock.ImagePtr + chan * index;
switch (Format)
{
default:
case QImageFormat.RedU8: return new QColorF(ptr[0], 0, 0, 255);
case QImageFormat.AlphaU8: return new QColorF(0, 0, 0, ptr[0]);
case QImageFormat.RaU8: return new QColorF(ptr[0], 0, 0, ptr[1]);
case QImageFormat.RgbU8: return new QColorF(ptr[0], ptr[1], ptr[2], 255);
case QImageFormat.RgbaU8: return new QColorF(ptr[0], ptr[1], ptr[2], ptr[3]);
}
}
set
{
int chan = Format.Channels();
float *ptr = (float*)Lock.ImagePtr + chan * index;
switch (Format)
{
default:
case QImageFormat.RedU8:
ptr[0] = value.R;
break;
case QImageFormat.AlphaU8:
ptr[0] = value.A;
break;
case QImageFormat.RaU8:
ptr[0] = value.R;
ptr[1] = value.A;
break;
case QImageFormat.RgbU8:
ptr[0] = value.R;
ptr[1] = value.G;
ptr[2] = value.B;
break;
case QImageFormat.RgbaU8:
ptr[0] = value.R;
ptr[1] = value.G;
ptr[2] = value.B;
ptr[3] = value.A;
break;
}
}
}
public QColorF this[int x, int y, int z = 0]
{
get => this[x + y * Width + z * Width * Height];
set => this[x + y * Width + z * Width * Height] = value;
}
}
}
-71
View File
@@ -1,71 +0,0 @@
namespace Dashboard.Media
{
public static class Extensions
{
public static bool IsU8(this QImageFormat format)
{
switch (format)
{
case QImageFormat.AlphaU8:
case QImageFormat.RedU8:
case QImageFormat.RaU8:
case QImageFormat.RgbU8:
case QImageFormat.RgbaU8:
return true;
default:
return false;
}
}
public static bool IsFloat(this QImageFormat format)
{
switch (format)
{
case QImageFormat.AlphaF:
case QImageFormat.RedF:
case QImageFormat.RaF:
case QImageFormat.RgbF:
case QImageFormat.RgbaF:
return true;
default:
return false;
}
}
public static int BytesPerPixel(this QImageFormat format)
{
switch (format)
{
case QImageFormat.AlphaU8: return sizeof(byte);
case QImageFormat.RedU8: return sizeof(byte);
case QImageFormat.RaU8: return 2 * sizeof(byte);
case QImageFormat.RgbU8: return 3 * sizeof(byte);
case QImageFormat.RgbaU8: return 4 * sizeof(byte);
case QImageFormat.AlphaF: return sizeof(float);
case QImageFormat.RedF: return sizeof(float);
case QImageFormat.RaF: return 2 * sizeof(float);
case QImageFormat.RgbF: return 3 * sizeof(float);
case QImageFormat.RgbaF: return 4 * sizeof(float);
default: return 0;
}
}
public static int Channels(this QImageFormat format)
{
switch (format)
{
case QImageFormat.AlphaU8: return 1;
case QImageFormat.RedU8: return 1;
case QImageFormat.RaU8: return 2;
case QImageFormat.RgbU8: return 3;
case QImageFormat.RgbaU8: return 4;
case QImageFormat.AlphaF: return 1;
case QImageFormat.RedF: return 1;
case QImageFormat.RaF: return 2;
case QImageFormat.RgbF: return 3;
case QImageFormat.RgbaF: return 4;
default: return 0;
}
}
}
}
-184
View File
@@ -1,184 +0,0 @@
using System;
using System.Collections.Generic;
using Dashboard.Media.Color;
namespace Dashboard.Media.Font
{
public struct FontAtlasGlyphInfo
{
public int Codepoint;
public QImage Image;
public QRectangle UVs;
}
public class FontAtlas
{
private readonly int width, height;
private readonly List<AtlasPage> atlases = new List<AtlasPage>();
private readonly Dictionary<int, FontAtlasGlyphInfo> glyphs = new Dictionary<int, FontAtlasGlyphInfo>();
private int index = 0;
private AtlasPage? last = null;
private bool isSdf = false;
private int expansion;
public bool IsSdf
{
get => isSdf;
set
{
foreach (AtlasPage page in atlases)
{
((QImageBuffer)page.Image).SetSdf(value);
}
isSdf = value;
}
}
public FontAtlas(int width, int height, bool isSdf, int expansion = 4)
{
this.width = width;
this.height = height;
IsSdf = isSdf;
this.expansion = expansion;
}
public bool GetGlyph(int codepoint, out FontAtlasGlyphInfo info)
{
return glyphs.TryGetValue(codepoint, out info);
}
public void PutGlyph(int codepoint, QImageLock source, out FontAtlasGlyphInfo info)
{
info = new FontAtlasGlyphInfo() { Codepoint = codepoint };
if (last == null || !last.WouldFit(source))
{
AddPage();
}
last!.PutGlyph(source, ref info);
}
private void AddPage()
{
index++;
if (index < atlases.Count)
{
last = atlases[index];
}
else
{
last = new AtlasPage(width, height, expansion);
((QImageBuffer)last.Image).SetSdf(IsSdf);
atlases.Add(last);
}
}
public void Clear()
{
// Trim any pages that were not used yet.
for (int i = atlases.Count -1; i >= 0; i--)
{
if (atlases[i].PointerX != 0 && atlases[i].PointerY != 0)
{
for (int j = i + 1; j < atlases.Count; j++)
{
atlases[j].Dispose();
}
if (i != atlases.Count - 1)
atlases.RemoveRange(i+1, atlases.Count - i - 1);
break;
}
}
if (atlases.Count > 0)
{
last = atlases[0];
}
else
{
last = null;
}
index = -1;
glyphs.Clear();
}
private class AtlasPage : IDisposable
{
public QImage Image;
public int PointerX, PointerY;
public int RowHeight;
public int Expansion;
public bool IsFull => PointerX > Image.Width || PointerY > Image.Height;
public AtlasPage(int width, int height, int expansion)
{
Image = new QImageBuffer(QImageFormat.AlphaU8, width, height);
Expansion = expansion;
Reset();
}
public void PutGlyph(QImageLock src, ref FontAtlasGlyphInfo prototype)
{
if (IsFull)
throw new Exception("Page is full!");
Image.LockBits2d(out QImageLock dst, QImageLockOptions.Default);
src.CopyTo(dst, PointerX, PointerY);
Image.UnlockBits();
QVec2 min = new QVec2((float)PointerX/Image.Width, (float)PointerY/Image.Height);
QVec2 size = new QVec2((float)src.Width/Image.Width, (float)src.Height/Image.Height);
prototype.Image = Image;
prototype.UVs = new QRectangle(min + size, min);
AdvanceColumn(src.Width, src.Height);
}
public void Reset()
{
RowHeight = PointerX = PointerY = 0;
}
public void AdvanceRow()
{
PointerX = 0;
PointerY += RowHeight + Expansion;
RowHeight = 0;
}
public void AdvanceColumn(int width, int height)
{
RowHeight = Math.Max(RowHeight, height);
PointerX += width + Expansion;
if (PointerX > Image.Width)
{
AdvanceRow();
}
}
private bool isDisposed = false;
public void Dispose()
{
if (isDisposed)
return;
Image?.Dispose();
isDisposed = true;
}
internal bool WouldFit(QImageLock source)
{
return !IsFull || PointerX + source.Width > Image.Width || PointerY + source.Height > Image.Height;
}
}
}
}
-238
View File
@@ -1,238 +0,0 @@
using System;
using System.Text;
namespace Dashboard.Media.Font
{
public readonly struct FontFace : IEquatable<FontFace>
{
public string Family { get; }
public FontSlant Slant { get; }
public FontWeight Weight { get; }
public FontStretch Stretch { get; }
public FontFace(string family, FontSlant slant, FontWeight weight, FontStretch stretch)
{
Family = family;
Slant = slant;
Weight = weight;
Stretch = stretch;
}
public override string ToString()
{
StringBuilder builder = new StringBuilder(Family);
if (Slant != FontSlant.Normal)
{
builder.Append(' ');
builder.Append(Slant);
}
if (Stretch != FontStretch.Normal)
{
builder.Append(' ');
builder.Append(Stretch);
}
if (Weight != FontWeight.Normal)
{
builder.Append(' ');
builder.Append(Weight);
}
if (Slant == FontSlant.Normal &&
Stretch == FontStretch.Normal &&
Weight == FontWeight.Normal)
{
builder.Append(" Regular");
}
return builder.ToString();
}
public override int GetHashCode()
{
return HashCode.Combine(Family, Slant, Weight, Stretch);
}
public static bool operator==(FontFace a, FontFace b)
{
return (a.Slant == b.Slant) &&
(a.Weight == b.Weight) &&
(a.Stretch == b.Stretch) &&
(a.Family == a.Family);
}
public static bool operator!=(FontFace a, FontFace b)
{
return (a.Slant != b.Slant) ||
(a.Weight != b.Weight) ||
(a.Stretch != b.Stretch) ||
(a.Family != b.Family);
}
public bool Equals(FontFace other)
{
return this == other;
}
public override bool Equals(object? obj)
{
return (obj?.GetType() == typeof(FontFace)) &&
this == (FontFace)obj;
}
public static FontFace Parse(string family, string style)
{
FontSlant slant = FontSlant.Normal;
FontWeight weight = FontWeight.Normal;
FontStretch stretch = FontStretch.Normal;
string[] tokens = style.Split(' ');
foreach (string token in tokens)
{
/**/ if (TryParseSlant(token, out FontSlant xslant)) slant = xslant;
else if (TryParseWeight(token, out FontWeight xweight)) weight = xweight;
else if (TryParseStretch(token, out FontStretch xstretch)) stretch = xstretch;
}
return new FontFace(family, slant, weight, stretch);
}
public static FontFace Parse(string face)
{
StringBuilder family = new StringBuilder();
FontSlant slant = FontSlant.Normal;
FontWeight weight = FontWeight.Normal;
FontStretch stretch = FontStretch.Normal;
string[] tokens = face.Split(' ');
foreach (string token in tokens)
{
string xtoken = token.ToLower();
if (xtoken == "regular" || xtoken == "normal")
{
continue;
}
else if (TryParseSlant(xtoken, out FontSlant xslant)) slant = xslant;
else if (TryParseWeight(xtoken, out FontWeight xweight)) weight = xweight;
else if (TryParseStretch(xtoken, out FontStretch xstretch)) stretch = xstretch;
else
{
family.Append(token);
}
}
return new FontFace(family.ToString(), slant, weight, stretch);
}
/// <summary>
/// Try to convert a token that represents a font slant into its enum.
/// </summary>
/// <param name="token">The token to interpret.</param>
/// <param name="slant">The resulting slant.</param>
/// <returns>True if it matched any.</returns>
public static bool TryParseSlant(string token, out FontSlant slant)
{
switch (token.ToLower())
{
case "italic":
slant = FontSlant.Italic;
return true;
case "oblique":
slant = FontSlant.Oblique;
return true;
default:
slant = FontSlant.Normal;
return false;
}
}
/// <summary>
/// Try to convert a token that represents a font weight into its enum.
/// </summary>
/// <param name="token">The token to interpret.</param>
/// <param name="weight">The resulting weight.</param>
/// <returns>True if it matched any.</returns>
public static bool TryParseWeight(string token, out FontWeight weight)
{
switch (token.ToLower())
{
case "thin":
weight = FontWeight.Thin;
return true;
case "extralight":
case "ultralight":
weight = FontWeight._200;
return true;
case "light":
case "demilight":
case "semilight":
weight = FontWeight._300;
return true;
case "demibold":
case "semibold":
weight = FontWeight._600;
return true;
case "bold":
weight = FontWeight._700;
return true;
case "extrabold":
case "ultrabold":
weight = FontWeight._800;
return true;
case "heavy":
case "extrablack":
case "black":
case "ultrablack":
weight = FontWeight._900;
return true;
default:
weight = FontWeight.Normal;
return false;
}
}
/// <summary>
/// Try to convert a token that represents a font stretch into its enum.
/// </summary>
/// <param name="token">The token to interpret.</param>
/// <param name="stretch">The resulting stretch.</param>
/// <returns>True if it matched any.</returns>
public static bool TryParseStretch(string token, out FontStretch stretch)
{
switch (token.ToLower())
{
case "ultracondensed":
stretch = FontStretch.UltraCondensed;
return true;
case "extracondensed":
stretch = FontStretch.ExtraCondensed;
return true;
case "condensed":
stretch = FontStretch.Condensed;
return true;
case "semicondensed":
case "demicondensed":
stretch = FontStretch.SemiCondensed;
return true;
case "semiexpanded":
case "demiexpanded":
stretch = FontStretch.SemiExpanded;
return true;
case "expanded":
stretch = FontStretch.Expanded;
return true;
case "extraexpanded":
stretch = FontStretch.ExtraExpanded;
return true;
case "ultraexpanded":
stretch = FontStretch.UltraExpanded;
return true;
default:
stretch = FontStretch.Normal;
return false;
}
}
}
}
-9
View File
@@ -1,9 +0,0 @@
namespace Dashboard.Media.Font
{
public enum FontSlant
{
Normal = 0,
Italic = 1,
Oblique = 2,
}
}
-18
View File
@@ -1,18 +0,0 @@
namespace Dashboard.Media.Font
{
/// <summary>
/// Enumeration of font stretch values.
/// </summary>
public enum FontStretch
{
UltraCondensed = 500,
ExtraCondensed = 625,
Condensed = 750,
SemiCondensed = 875,
Normal = 1000,
SemiExpanded = 1125,
Expanded = 1250,
ExtraExpanded = 1500,
UltraExpanded = 2000,
}
}
-22
View File
@@ -1,22 +0,0 @@
using System;
namespace Dashboard.Media.Font
{
public enum FontWeight
{
_100 = 100,
_200 = 200,
_300 = 300,
_400 = 400,
_500 = 500,
_600 = 600,
_700 = 700,
_800 = 800,
_900 = 900,
Thin = _100,
Normal = _400,
Bold = _700,
Heavy = _900,
}
}
-26
View File
@@ -1,26 +0,0 @@
namespace Dashboard.Media.Font
{
public enum SystemFontFamily
{
/// <summary>
/// A font with serifs, like Times New Roman.
/// </summary>
Serif,
/// <summary>
/// A font without serifs, like Helvetica or Arial.
/// </summary>
Sans,
/// <summary>
/// A monospace font like Courier New.
/// </summary>
Monospace,
/// <summary>
/// A cursive font like Lucida Handwriting.
/// </summary>
Cursive,
/// <summary>
/// An immature font like Comic Sans or Papyrus, nghehehehe.
/// </summary>
Fantasy
}
}
-19
View File
@@ -1,19 +0,0 @@
using System;
namespace Dashboard.Media
{
public enum QImageFormat
{
Undefined,
RedU8,
RgbU8,
RgbaU8,
RedF,
RgbF,
RgbaF,
AlphaU8,
AlphaF,
RaU8,
RaF,
}
}
-22
View File
@@ -1,22 +0,0 @@
using System;
using System.IO;
namespace Dashboard.Media
{
public enum MediaHint
{
None,
Image,
Font
}
public interface MediaLoader
{
IDisposable GetMedia(object key, MediaHint hint);
}
public interface MediaLoader<T> : MediaLoader
{
IDisposable GetMedia(T key, MediaHint hint);
}
}
-129
View File
@@ -1,129 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Dashboard.Media;
using Dashboard.Media.Font;
namespace Dashboard.Media
{
/// <summary>
/// Abstract class that represents a font.
/// </summary>
public abstract class QFont : IDisposable
{
public abstract FontFace Face { get; }
public string Family => Face.Family;
public FontSlant Slant => Face.Slant;
public FontWeight Weight => Face.Weight;
public FontStretch Stretch => Face.Stretch;
public abstract bool HasRune(int rune);
protected abstract QImage Render(out QGlyphMetrics metrics, int codepoint, float size, in FontRasterizerOptions options);
private readonly Dictionary<float, SizedFontCollection> _atlasses = new Dictionary<float, SizedFontCollection>();
public void Get(int codepoint, float size, out FontGlyph glyph)
{
SizedFontCollection? collection;
if (!_atlasses.TryGetValue(size, out collection))
{
collection = new SizedFontCollection(size);
_atlasses.Add(size, collection);
}
collection.Get(codepoint, out glyph, this);
}
// IDisposable
private bool isDisposed = false;
private void DisposePrivate(bool disposing)
{
if (isDisposed) return;
Dispose(disposing);
isDisposed = true;
}
protected virtual void Dispose(bool disposing) { }
public void Dispose() => DisposePrivate(true);
private class SizedFontCollection
{
public float Size { get; }
private readonly Dictionary<int, FontGlyph> glyphs = new Dictionary<int, FontGlyph>();
private readonly FontAtlas atlas;
public SizedFontCollection(float size)
{
Size = size;
DashboardApplication.Current.Platform.GetMaximumImage(out int height, out int width);
// Do no allow to create a texture that is greater than 16 square characters at 200 DPI.
width = Math.Min(width, (int)(size * 200 * 16));
height = Math.Min(height, (int)(size * 200 * 16));
// width = height = 256;
atlas = new FontAtlas(width, height, DashboardApplication.Current.FontProvider.RasterizerOptions.Sdf);
}
public void Get(int codepoint, out FontGlyph glyph, QFont font)
{
if (glyphs.TryGetValue(codepoint, out glyph))
return;
QImage image = font.Render(
out QGlyphMetrics metrics,
codepoint,
Size,
DashboardApplication.Current.FontProvider.RasterizerOptions);
if (image != null)
{
image.LockBits2d(out QImageLock l, QImageLockOptions.Default);
atlas.PutGlyph(codepoint, l, out FontAtlasGlyphInfo glyphInfo);
image.UnlockBits();
image.Dispose();
glyph = new FontGlyph(codepoint, glyphInfo.Image, metrics, glyphInfo.UVs);
}
else
{
glyph = new FontGlyph(codepoint, null, metrics, default);
}
glyphs[codepoint] = glyph;
}
}
}
public readonly struct FontGlyph
{
public readonly int CodePoint;
public readonly QImage? Image;
public readonly QGlyphMetrics Metrics;
public readonly QRectangle UVs;
public FontGlyph(int codepoint, QImage? image, in QGlyphMetrics metrics, in QRectangle uvs)
{
CodePoint = codepoint;
Image = image;
Metrics = metrics;
UVs = uvs;
}
}
public struct FontRasterizerOptions
{
public float Resolution { get; set; }
public bool Sdf { get; set; }
public static readonly FontRasterizerOptions Default = new FontRasterizerOptions()
{
Resolution = 96.0f,
Sdf = false
};
}
}
-47
View File
@@ -1,47 +0,0 @@
namespace Dashboard.Media
{
/// <summary>
/// Glyph properties with metrics based on FreeType glyph metrics.
/// </summary>
public struct QGlyphMetrics
{
/// <summary>
/// The code point for the character.
/// </summary>
public int Rune { get; }
/// <summary>
/// Size of the glyph in units.
/// </summary>
public QVec2 Size { get; }
/// <summary>
/// Bearing vector for horizontal layout.
/// </summary>
public QVec2 HorizontalBearing { get; }
/// <summary>
/// Bearing vector for vertical layout.
/// </summary>
public QVec2 VerticalBearing { get; }
/// <summary>
/// Advance vector for vertical and horizontal layouts.
/// </summary>
public QVec2 Advance { get; }
public QGlyphMetrics(
int character,
QVec2 size,
QVec2 horizontalBearing,
QVec2 verticalBearing,
QVec2 advance)
{
Rune = character;
Size = size;
HorizontalBearing = horizontalBearing;
VerticalBearing = verticalBearing;
Advance = advance;
}
}
}
-120
View File
@@ -1,120 +0,0 @@
using System;
namespace Dashboard.Media
{
public abstract class QImage : IDisposable
{
public abstract int Width { get; }
public abstract int Height { get; }
public abstract int Depth { get; }
public abstract QImageFormat InternalFormat { get; }
public virtual int MipMapLevels => 0;
public virtual bool Premultiplied => false;
public virtual bool IsSdf => false;
public abstract void LockBits2d(out QImageLock imageLock, QImageLockOptions options);
public abstract void LockBits3d(out QImageLock imageLock, QImageLockOptions options);
public abstract void LockBits3d(out QImageLock imageLock, QImageLockOptions options, int depth);
public abstract void UnlockBits();
// IDisposable
private bool isDisposed = false;
private void DisposePrivate(bool disposing)
{
if (isDisposed) return;
Dispose(disposing);
isDisposed = true;
}
protected virtual void Dispose(bool disposing) { }
public void Dispose() => DisposePrivate(true);
}
public struct QImageLockOptions
{
public QImageFormat Format { get; }
public bool Premultiply { get; }
public int MipLevel { get; }
public static QImageLockOptions Default { get; } = new QImageLockOptions(QImageFormat.RgbaU8, true, 0);
public QImageLockOptions(QImageFormat format, bool premultiply, int level)
{
Format = format;
Premultiply = premultiply;
MipLevel = level;
}
}
public struct QImageLock
{
public QImageFormat Format { get; }
public int Width { get; }
public int Height { get; }
public int Depth { get; }
public IntPtr ImagePtr { get; }
public QImageLock(QImageFormat format, int width, int height, int depth, IntPtr ptr)
{
Format = format;
Width = width;
Height = height;
Depth = depth;
ImagePtr = ptr;
}
public unsafe void CopyTo(QImageLock destination, int x, int y)
{
if (
Width + x > destination.Width ||
Height + y > destination.Height)
{
throw new Exception("Image falls outside the bounds of the destination.");
}
else if (Format != destination.Format)
{
throw new Exception("Image formats must be the same.");
}
int bpp = Format.BytesPerPixel();
for (int i = 0; i < Height; i++)
{
IntPtr srcPtr = (IntPtr)((long)ImagePtr + i * Width * bpp);
long dstPos = x + i * destination.Width;
IntPtr dstPtr = (IntPtr)((long)destination.ImagePtr + dstPos * bpp);
Buffer.MemoryCopy((void*)srcPtr, (void*)dstPtr, Width * bpp, Width * bpp);
}
}
public unsafe void ExtractFrom(QImageLock destination, int x, int y, int width, int height)
{
if (
width != destination.Width ||
height != destination.Height)
{
throw new Exception("Destination is not the same size as the subregion.");
}
else if (x + width > Width || y + height > Height)
{
throw new Exception("The subregion is larger than this image.");
}
else if (Format != destination.Format)
{
throw new Exception("Image formats must be the same.");
}
int bpp = Format.BytesPerPixel();
for (int i = 0; i < height; i++)
{
long srcPos = x + y * i;
IntPtr srcPtr = (IntPtr)((long)ImagePtr + srcPos * bpp);
long dstPos = i * destination.Width;
IntPtr dstPtr = (IntPtr)((long)destination.ImagePtr + dstPos * bpp);
Buffer.MemoryCopy((void*)srcPtr, (void*)dstPtr, width * bpp, width * bpp);
}
}
}
}
-74
View File
@@ -1,74 +0,0 @@
using System;
namespace Dashboard
{
public enum MouseButton : byte
{
Primary = 1 << 0,
Secondary = 1 << 1,
Tertiary = 1 << 2,
Auxilliary1 = 1 << 3,
Auxilliary2 = 1 << 4,
Auxilliary3 = 1 << 5,
Auxilliary4 = 1 << 6,
Auxilliary5 = 1 << 7,
}
public struct MouseState
{
public readonly QVec2 AbsolutePosition;
public readonly MouseButton ButtonsDown;
public MouseState(QVec2 position, MouseButton down)
{
AbsolutePosition = position;
ButtonsDown = down;
}
}
public class MouseButtonEventArgs : EventArgs
{
public QVec2 AbsolutePosition { get; }
public MouseButton Buttons { get; }
public MouseButtonEventArgs(QVec2 position, MouseButton buttons)
{
AbsolutePosition = position;
Buttons = buttons;
}
public QVec2 RelativePosition(QVec2 origin)
{
return AbsolutePosition - origin;
}
// public QVec2 RelativePosition(Controls.Control control)
// {
// return AbsolutePosition - control.AbsoluteBounds.Min;
// }
}
public class MouseMoveEventArgs : EventArgs
{
public QVec2 AbsolutePosition { get; }
public QVec2 LastPosition { get; }
public QVec2 Motion { get; }
public MouseMoveEventArgs(QVec2 position, QVec2 lastPosition)
{
AbsolutePosition = position;
LastPosition = lastPosition;
Motion = position - lastPosition;
}
public QVec2 RelativePosition(QVec2 origin)
{
return AbsolutePosition - origin;
}
// public QVec2 RelativePosition(Controls.Control control)
// {
// return AbsolutePosition - control.AbsoluteBounds.Min;
// }
}
}
-77
View File
@@ -1,77 +0,0 @@
using System;
using System.Runtime.CompilerServices;
namespace Dashboard.OpenGL
{
public unsafe static partial class GL
{
private delegate void BufferDataProc(GLEnum target, int size, void* data, GLEnum usageHint);
private static GenObjectsProc? _genBuffers;
private static GenObjectsProc? _deleteBuffers;
private static BindSlottedProc? _bindBuffer;
private static BufferDataProc? _bufferData;
private static void LoadBuffer()
{
_genBuffers = GetProcAddress<GenObjectsProc>("glGenBuffers");
_deleteBuffers = GetProcAddress<GenObjectsProc>("glDeleteBuffers");
_bindBuffer = GetProcAddress<BindSlottedProc>("glBindBuffer");
_bufferData = GetProcAddress<BufferDataProc>("glBufferData");
}
[MethodImpl(AggressiveInlining)]
public static void GenBuffers(int count, out int buffers)
{
fixed (int *ptr = &buffers)
_genBuffers!(count, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void GenBuffers(int[] buffers) => GenBuffers(buffers.Length, out buffers[0]);
[MethodImpl(AggressiveInlining)]
public static int GenBuffer()
{
GenBuffers(1, out int i);
return i;
}
[MethodImpl(AggressiveInlining)]
public static void DeleteBuffers(int count, ref int buffers)
{
fixed (int *ptr = &buffers)
_deleteBuffers!(count, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void DeleteBuffers(int[] buffers) => DeleteBuffers(buffers.Length, ref buffers[0]);
[MethodImpl(AggressiveInlining)]
public static void DeleteBuffer(int buffer) => DeleteBuffers(1, ref buffer);
[MethodImpl(AggressiveInlining)]
public static void BindBuffer(GLEnum target, int buffer)
{
_bindBuffer!(target, buffer);
}
[MethodImpl(AggressiveInlining)]
public static void BufferData(GLEnum target, int size, IntPtr data, GLEnum usageHint) =>
_bufferData!(target, size, (void*)data, usageHint);
[MethodImpl(AggressiveInlining)]
public static void BufferData<T>(GLEnum target, int size, ref T data, GLEnum usageHint)
where T : unmanaged
{
fixed (T* ptr = &data)
_bufferData!(target, size, ptr, usageHint);
}
[MethodImpl(AggressiveInlining)]
public static void BufferData<T>(GLEnum target, int size, T[] data, GLEnum usageHint)
where T : unmanaged =>
BufferData(target, size, ref data[0], usageHint);
}
}
-96
View File
@@ -1,96 +0,0 @@
using System.Runtime.CompilerServices;
using System.Text;
using static Dashboard.OpenGL.GLEnum;
namespace Dashboard.OpenGL
{
public unsafe static partial class GL
{
private delegate int CreateProgramProc();
private delegate void UseProgramProc(int program);
private delegate void AttachShaderProc(int program, int shader);
private delegate void DetachShaderProc(int program, int shader);
private delegate void LinkProgramProc(int program);
private delegate void GetProgramProc(int program, GLEnum pname, int *value);
private delegate void GetProgramInfoLogProc(int program, int maxLength, int * length, byte *infoLog);
private delegate void DeleteProgramProc(int program);
private delegate int GetShaderLocationProc(int program, byte *name);
private static CreateProgramProc? _createProgram;
private static UseProgramProc? _useProgram;
private static AttachShaderProc? _attachShader;
private static DetachShaderProc? _detachShader;
private static LinkProgramProc? _linkProgram;
private static GetProgramProc? _getProgram;
private static GetProgramInfoLogProc? _getProgramInfoLog;
private static DeleteProgramProc? _deleteProgram;
private static GetShaderLocationProc? _getUniformLocation;
private static GetShaderLocationProc? _getAttribLocation;
private static void LoadProgram()
{
_createProgram = GetProcAddress<CreateProgramProc>("glCreateProgram");
_useProgram = GetProcAddress<UseProgramProc>("glUseProgram");
_attachShader = GetProcAddress<AttachShaderProc>("glAttachShader");
_detachShader = GetProcAddress<DetachShaderProc>("glDetachShader");
_linkProgram = GetProcAddress<LinkProgramProc>("glLinkProgram");
_getProgram = GetProcAddress<GetProgramProc>("glGetProgramiv");
_getProgramInfoLog = GetProcAddress<GetProgramInfoLogProc>("glGetProgramInfoLog");
_deleteProgram = GetProcAddress<DeleteProgramProc>("glDeleteProgram");
_getUniformLocation = GetProcAddress<GetShaderLocationProc>("glGetUniformLocation");
_getAttribLocation = GetProcAddress<GetShaderLocationProc>("glGetAttribLocation");
}
[MethodImpl(AggressiveInlining)]
public static int CreateProgram() => _createProgram!();
[MethodImpl(AggressiveInlining)]
public static void UseProgram(int program) => _useProgram!(program);
[MethodImpl(AggressiveInlining)]
public static void AttachShader(int program, int shader) => _attachShader!(program, shader);
[MethodImpl(AggressiveInlining)]
public static void DetachShader(int program, int shader) => _detachShader!(program, shader);
[MethodImpl(AggressiveInlining)]
public static void LinkProgram(int program) => _linkProgram!(program);
[MethodImpl(AggressiveInlining)]
public static void GetProgram(int program, GLEnum pname, out int value)
{
value = default;
fixed (int* ptr = &value)
_getProgram!(program, pname, ptr);
}
[MethodImpl(AggressiveInlining)]
public static string GetProgramInfoLog(int program)
{
GetProgram(program, GL_INFO_LOG_LENGTH, out int length);
byte[] infoLog = new byte[length];
fixed (byte *ptr = infoLog)
_getProgramInfoLog!(program, length, &length, ptr);
return Encoding.UTF8.GetString(infoLog);
}
[MethodImpl(AggressiveInlining)]
public static void DeleteProgram(int program) => _deleteProgram!(program);
[MethodImpl(AggressiveInlining)]
public static int GetUniformLocation(int program, string name)
{
fixed(byte* ptr = Encoding.UTF8.GetBytes(name))
return _getUniformLocation!(program, ptr);
}
[MethodImpl(AggressiveInlining)]
public static int GetAttribLocation(int program, string name)
{
fixed(byte* ptr = Encoding.UTF8.GetBytes(name))
return _getAttribLocation!(program, ptr);
}
}
}
-110
View File
@@ -1,110 +0,0 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using static Dashboard.OpenGL.GLEnum;
namespace Dashboard.OpenGL
{
public unsafe static partial class GL
{
private delegate int CreateShaderProc(GLEnum type);
private delegate void ShaderSourceProc(int shader, int count, byte** strings, int* length);
private delegate void CompileShaderProc(int shader);
private delegate void GetShaderProc(int shader, GLEnum pname, int* value);
private delegate void GetShaderInfoLogProc(int shader, int maxLength, int* length, byte* infoLog);
private delegate void DeleteShaderProc(int id);
private static CreateShaderProc? _createShader;
private static ShaderSourceProc? _shaderSource;
private static CompileShaderProc? _compileShader;
private static GetShaderProc? _getShader;
private static GetShaderInfoLogProc? _getShaderInfoLog;
private static DeleteShaderProc? _deleteShader;
private static void LoadShader()
{
_createShader = GetProcAddress<CreateShaderProc>("glCreateShader");
_shaderSource = GetProcAddress<ShaderSourceProc>("glShaderSource");
_compileShader = GetProcAddress<CompileShaderProc>("glCompileShader");
_getShader = GetProcAddress<GetShaderProc>("glGetShaderiv");
_getShaderInfoLog = GetProcAddress<GetShaderInfoLogProc>("glGetShaderInfoLog");
_deleteShader = GetProcAddress<DeleteShaderProc>("glDeleteShader");
}
[MethodImpl(AggressiveInlining)]
public static int CreateShader(GLEnum type) => _createShader!(type);
[MethodImpl(AggressiveInlining)]
public static void ShaderSource(int shader, string source)
{
byte[] sourceUTF8 = Encoding.UTF8.GetBytes(source);
int length = sourceUTF8.Length;
fixed (byte* ptr = &sourceUTF8[0])
{
_shaderSource!(shader, 1, &ptr, &length);
}
}
[MethodImpl(AggressiveInlining)]
public static void ShaderSource(int shader, string[] sources)
{
int count = sources.Length;
byte*[] pointers = new byte*[count];
int[] lengths = new int[count];
for (int i = 0; i < count; i++)
{
byte[] decoded = Encoding.UTF8.GetBytes(sources[i]);
int length = lengths[i] = decoded.Length;
IntPtr memory = Marshal.AllocHGlobal(decoded.Length);
Marshal.Copy(decoded, 0, memory, length);
pointers[i] = (byte*)memory;
}
try
{
fixed (byte** ptr = &pointers[0])
fixed (int * len = &lengths[0])
{
_shaderSource!(shader, count, ptr, len);
}
}
finally
{
for (int i = 0; i < count; i++)
{
Marshal.FreeHGlobal((IntPtr)pointers[i]);
}
}
}
[MethodImpl(AggressiveInlining)]
public static void CompileShader(int shader) => _compileShader!(shader);
[MethodImpl(AggressiveInlining)]
public static void GetShader(int shader, GLEnum pname, out int value)
{
value = default;
fixed (int *ptr = &value)
_getShader!(shader, pname, ptr);
}
[MethodImpl(AggressiveInlining)]
public static string GetShaderInfoLog(int shader)
{
GetShader(shader, GL_INFO_LOG_LENGTH, out int length);
byte[] infoLog = new byte[length];
fixed (byte *ptr = infoLog)
_getShaderInfoLog!(shader, length, &length, ptr);
return Encoding.UTF8.GetString(infoLog);
}
[MethodImpl(AggressiveInlining)]
public static void DeleteShader(int shader) => _deleteShader!(shader);
}
}
-187
View File
@@ -1,187 +0,0 @@
using System;
using System.Runtime.CompilerServices;
namespace Dashboard.OpenGL
{
public unsafe static partial class GL
{
private delegate void ActiveTextureProc(GLEnum unit);
private delegate void PixelStoreiProc(GLEnum pname, int param);
private delegate void PixelStorefProc(GLEnum pname, float param);
private delegate void TexImage2DProc(GLEnum target, int level, GLEnum internalFormat, int width, int height, int border, GLEnum format, GLEnum pixelType, void *data);
private delegate void TexImage3DProc(GLEnum target, int level, GLEnum internalFormat, int width, int height, int depth, int border, GLEnum format, GLEnum pixelType, void* data);
private delegate void TexSubImage2DProc(GLEnum target, int level, int x, int y, int width, int height, GLEnum format, GLEnum pixelType, void *data);
private delegate void TexSubImage3DProc(GLEnum target, int level, int x, int y, int z, int width, int height, int depth, int border, GLEnum format, GLEnum pixelType, void* data);
private delegate void TexParameteriProc(GLEnum target, GLEnum pname, int value);
private delegate void TexParameterfProc(GLEnum target, GLEnum pname, float value);
private delegate void TexParameterivProc(GLEnum target, GLEnum pname, int* value);
private delegate void TexParameterfvProc(GLEnum target, GLEnum pname, float* value);
private delegate void GenerateMipmapProc(GLEnum target);
private static GenObjectsProc? _genTextures;
private static GenObjectsProc? _deleteTextures;
private static BindSlottedProc? _bindTexture;
private static ActiveTextureProc? _activeTexture;
private static PixelStoreiProc? _pixelStorei;
private static PixelStorefProc? _pixelStoref;
private static TexImage2DProc? _texImage2D;
private static TexImage3DProc? _texImage3D;
private static TexSubImage2DProc? _texSubImage2D;
private static TexSubImage3DProc? _texSubImage3D;
private static TexParameteriProc? _texParameteri;
private static TexParameterfProc? _texParameterf;
private static TexParameterivProc? _texParameteriv;
private static TexParameterfvProc? _texParameterfv;
private static GenerateMipmapProc? _generateMipmap;
private static void LoadTexture()
{
_genTextures = GetProcAddress<GenObjectsProc>("glGenTextures");
_deleteTextures = GetProcAddress<GenObjectsProc>("glDeleteTextures");
_bindTexture = GetProcAddress<BindSlottedProc>("glBindTexture");
_activeTexture = GetProcAddress<ActiveTextureProc>("glActiveTexture");
_pixelStorei = GetProcAddress<PixelStoreiProc>("glPixelStorei");
_pixelStoref = GetProcAddress<PixelStorefProc>("glPixelStoref");
_texImage2D = GetProcAddress<TexImage2DProc>("glTexImage2D");
_texImage3D = GetProcAddress<TexImage3DProc>("glTexImage3D");
_texSubImage2D = GetProcAddress<TexSubImage2DProc>("glTexSubImage2D");
_texSubImage3D = GetProcAddress<TexSubImage3DProc>("glTexSubImage3D");
_texParameteri = GetProcAddress<TexParameteriProc>("glTexParameteri");
_texParameterf = GetProcAddress<TexParameterfProc>("glTexParameterf");
_texParameteriv = GetProcAddress<TexParameterivProc>("glTexParameteriv");
_texParameterfv = GetProcAddress<TexParameterfvProc>("glTexParameterfv");
_generateMipmap = GetProcAddress<GenerateMipmapProc>("glGenerateMipmap");
}
[MethodImpl(AggressiveInlining)]
public static void GenTextures(int count, out int textures)
{
fixed (int *ptr = &textures)
_genTextures!(count, ptr);
}
[MethodImpl(AggressiveInlining)]
public static int GenTexture()
{
GenTextures(1, out int i);
return i;
}
[MethodImpl(AggressiveInlining)]
public static void GenTextures(int[] textures) => GenTextures(textures.Length, out textures[0]);
[MethodImpl(AggressiveInlining)]
public static void DeleteTextures(int count, in int textures)
{
fixed (int* ptr = &textures)
_deleteTextures!(count, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void DeleteTexture(int i) => DeleteTextures(1, i);
[MethodImpl(AggressiveInlining)]
public static void DeleteTextures(int[] textures) => DeleteTextures(textures.Length, in textures[0]);
[MethodImpl(AggressiveInlining)]
public static void BindTexture(GLEnum target, int texture) => _bindTexture!(target, texture);
[MethodImpl(AggressiveInlining)]
public static void ActiveTexture(GLEnum unit) => _activeTexture!(unit);
[MethodImpl(AggressiveInlining)]
public static void PixelStore(GLEnum pname, int value) => _pixelStorei!(pname, value);
[MethodImpl(AggressiveInlining)]
public static void PixelStore(GLEnum pname, float value) => _pixelStoref!(pname, value);
[MethodImpl(AggressiveInlining)]
public static void TexImage2D(GLEnum target, int level, GLEnum internalFormat, int width, int height, int border, GLEnum format, GLEnum pixelType, IntPtr data) =>
_texImage2D!(target, level, internalFormat, width, height, border, format, pixelType, (void*)data);
[MethodImpl(AggressiveInlining)]
public static void TexImage2D<T>(GLEnum target, int level, GLEnum internalFormat, int width, int height, int border, GLEnum format, GLEnum pixelType, in T data) where T : unmanaged
{
fixed(T *ptr = &data)
_texImage2D!(target, level, internalFormat, width, height, border, format, pixelType, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void TexImage2D<T>(GLEnum target, int level, GLEnum internalFormat, int width, int height, int border, GLEnum format, GLEnum pixelType, T[] data) where T : unmanaged =>
TexImage2D(target, level, internalFormat, width, height, border, format, pixelType, in data[0]);
[MethodImpl(AggressiveInlining)]
public static void TexImage3D(GLEnum target, int level, GLEnum internalFormat, int width, int height, int depth, int border, GLEnum format, GLEnum pixelType, void* data) =>
_texImage3D!(target, level, internalFormat, width, height, depth, border, format, pixelType, data);
[MethodImpl(AggressiveInlining)]
public static void TexImage3D<T>(GLEnum target, int level, GLEnum internalFormat, int width, int height, int depth, int border, GLEnum format, GLEnum pixelType, in T data)
where T : unmanaged
{
fixed (T* ptr = &data)
_texImage3D!(target, level, internalFormat, width, height, depth, border, format, pixelType, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void TexImage3D<T>(GLEnum target, int level, GLEnum internalFormat, int width, int height, int depth, int border, GLEnum format, GLEnum pixelType, T[] data)
where T : unmanaged =>
TexImage3D(target, level, internalFormat, width, height, depth, border, format, pixelType, in data[0]);
[MethodImpl(AggressiveInlining)]
public static void TexSubImage2D(GLEnum target, int level, int x, int y, int width, int height, GLEnum format, GLEnum pixelType, IntPtr data) =>
_texSubImage2D!(target, level, x, y, width, height,format, pixelType, (void*)data);
[MethodImpl(AggressiveInlining)]
public static void TexSubImage2d<T>(GLEnum target, int level, int x, int y, int width, int height, GLEnum format, GLEnum pixelType, in T data) where T : unmanaged
{
fixed(T *ptr = &data)
_texSubImage2D!(target, level, x, y, width, height, format, pixelType, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void TexSubImage2D<T>(GLEnum target, int level, int x, int y, int width, int height, GLEnum format, GLEnum pixelType, T[] data) where T : unmanaged =>
TexSubImage2d<T>(target, level, x, y, width, height, format, pixelType, in data[0]);
[MethodImpl(AggressiveInlining)]
public static void TexSubImage3D(GLEnum target, int level, int x, int y, int z, int width, int height, int depth, int border, GLEnum format, GLEnum pixelType, void* data) =>
_texSubImage3D!(target, level, x, y, z, width, height, depth, border, format, pixelType, data);
[MethodImpl(AggressiveInlining)]
public static void TexSubImage3D<T>(GLEnum target, int level, int x, int y, int z, int width, int height, int depth, int border, GLEnum format, GLEnum pixelType, in T data)
where T : unmanaged
{
fixed (T* ptr = &data)
_texSubImage3D!(target, level, x, y, z, width, height, depth, border, format, pixelType, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void TexSubImage3D<T>(GLEnum target, int level, int x, int y, int z, int width, int height, int depth, int border, GLEnum format, GLEnum pixelType, T[] data)
where T : unmanaged =>
TexSubImage3D(target, level, x, y, z, width, height, depth, border, format, pixelType, in data[0]);
[MethodImpl(AggressiveInlining)]
public static void TexParameter(GLEnum target, GLEnum pname, int value) => _texParameteri!(target, pname, value);
[MethodImpl(AggressiveInlining)]
public static void TexParameter(GLEnum target, GLEnum pname, GLEnum value) => _texParameteri!(target, pname, (int)value);
[MethodImpl(AggressiveInlining)]
public static void TexParameter(GLEnum target, GLEnum pname, float value) => _texParameterf!(target, pname, value);
[MethodImpl(AggressiveInlining)]
public static void TexParameter(GLEnum target, GLEnum pname, ref int values)
{
fixed (int *ptr = &values)
_texParameteriv!(target, pname, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void TexParameter(GLEnum target, GLEnum pname, int[] values) => TexParameter(target, pname, ref values[0]);
[MethodImpl(AggressiveInlining)]
public static void TexParameter(GLEnum target, GLEnum pname, ref float values)
{
fixed (float *ptr = &values)
_texParameterfv!(target, pname, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void TexParameter(GLEnum target, GLEnum pname, float[] values) => TexParameter(target, pname, ref values[0]);
[MethodImpl(AggressiveInlining)]
public static void GenerateMipmap(GLEnum target) => _generateMipmap!(target);
}
}
-222
View File
@@ -1,222 +0,0 @@
using System.Runtime.CompilerServices;
namespace Dashboard.OpenGL
{
public unsafe static partial class GL
{
private delegate void Uniform1fProc(int location, float x);
private delegate void Uniform2fProc(int location, float x, float y);
private delegate void Uniform3fProc(int location, float x, float y, float z);
private delegate void Uniform4fProc(int location, float x, float y, float z, float w);
private delegate void Uniform1iProc(int location, int x);
private delegate void Uniform2iProc(int location, int x, int y);
private delegate void Uniform3iProc(int location, int x, int y, int z);
private delegate void Uniform4iProc(int location, int x, int y, int z, int w);
private delegate void UniformNfvProc(int location, int count, float* value);
private delegate void UniformNivProc(int location, int count, int* value);
private delegate void UniformMatrixNxNfvProc(int location, int count, bool transpose, float *value);
private static Uniform1fProc? _uniform1f;
private static Uniform2fProc? _uniform2f;
private static Uniform3fProc? _uniform3f;
private static Uniform4fProc? _uniform4f;
private static Uniform1iProc? _uniform1i;
private static Uniform2iProc? _uniform2i;
private static Uniform3iProc? _uniform3i;
private static Uniform4iProc? _uniform4i;
private static UniformNfvProc? _uniform1fv;
private static UniformNfvProc? _uniform2fv;
private static UniformNfvProc? _uniform3fv;
private static UniformNfvProc? _uniform4fv;
private static UniformNivProc? _uniform1iv;
private static UniformNivProc? _uniform2iv;
private static UniformNivProc? _uniform3iv;
private static UniformNivProc? _uniform4iv;
private static UniformMatrixNxNfvProc? _uniformMatrix2fv;
private static UniformMatrixNxNfvProc? _uniformMatrix3fv;
private static UniformMatrixNxNfvProc? _uniformMatrix4fv;
public static void LoadUniform()
{
_uniform1f = GetProcAddress<Uniform1fProc>("glUniform1f");
_uniform2f = GetProcAddress<Uniform2fProc>("glUniform2f");
_uniform3f = GetProcAddress<Uniform3fProc>("glUniform3f");
_uniform4f = GetProcAddress<Uniform4fProc>("glUniform4f");
_uniform1i = GetProcAddress<Uniform1iProc>("glUniform1i");
_uniform2i = GetProcAddress<Uniform2iProc>("glUniform2i");
_uniform3i = GetProcAddress<Uniform3iProc>("glUniform3i");
_uniform4i = GetProcAddress<Uniform4iProc>("glUniform4i");
_uniform1fv = GetProcAddress<UniformNfvProc>("glUniform1fv");
_uniform2fv = GetProcAddress<UniformNfvProc>("glUniform2fv");
_uniform3fv = GetProcAddress<UniformNfvProc>("glUniform3fv");
_uniform4fv = GetProcAddress<UniformNfvProc>("glUniform4fv");
_uniform1iv = GetProcAddress<UniformNivProc>("glUniform1iv");
_uniform2iv = GetProcAddress<UniformNivProc>("glUniform2iv");
_uniform3iv = GetProcAddress<UniformNivProc>("glUniform3iv");
_uniform4iv = GetProcAddress<UniformNivProc>("glUniform4iv");
_uniformMatrix2fv = GetProcAddress<UniformMatrixNxNfvProc>("glUniformMatrix2fv");
_uniformMatrix3fv = GetProcAddress<UniformMatrixNxNfvProc>("glUniformMatrix3fv");
_uniformMatrix4fv = GetProcAddress<UniformMatrixNxNfvProc>("glUniformMatrix4fv");
}
[MethodImpl(AggressiveInlining)]
public static void Uniform1(int location, float x)
{
_uniform1f!(location, x);
}
[MethodImpl(AggressiveInlining)]
public static void Uniform2(int location, float x, float y)
{
_uniform2f!(location, x, y);
}
[MethodImpl(AggressiveInlining)]
public static void Uniform3(int location, float x, float y, float z)
{
_uniform3f!(location, x, y, z);
}
[MethodImpl(AggressiveInlining)]
public static void Uniform4(int location, float x, float y, float z, float w)
{
_uniform4f!(location, x, y, z, w);
}
[MethodImpl(AggressiveInlining)]
public static void Uniform1(int location, int x)
{
_uniform1i!(location, x);
}
[MethodImpl(AggressiveInlining)]
public static void Uniform2(int location, int x, int y)
{
_uniform2i!(location, x, y);
}
[MethodImpl(AggressiveInlining)]
public static void Uniform3(int location, int x, int y, int z)
{
_uniform3i!(location, x, y, z);
}
[MethodImpl(AggressiveInlining)]
public static void Uniform4(int location, int x, int y, int z, int w)
{
_uniform4i!(location, x, y, z, w);
}
[MethodImpl(AggressiveInlining)]
public static void Uniform1(int location, int count, ref float first)
{
fixed(float *ptr = &first)
_uniform1fv!(location, count, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void Uniform2(int location, int count, ref float first)
{
fixed(float *ptr = &first)
_uniform2fv!(location, count, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void Uniform3(int location, int count, ref float first)
{
fixed(float *ptr = &first)
_uniform3fv!(location, count, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void Uniform4(int location, int count, ref float first)
{
fixed(float *ptr = &first)
_uniform4fv!(location, count, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void Uniform1(int location, int count, ref int first)
{
fixed(int *ptr = &first)
_uniform1iv!(location, count, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void Uniform2(int location, int count, ref int first)
{
fixed(int *ptr = &first)
_uniform2iv!(location, count, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void Uniform3(int location, int count, ref int first)
{
fixed(int *ptr = &first)
_uniform3iv!(location, count, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void Uniform4(int location, int count, ref int first)
{
fixed(int *ptr = &first)
_uniform4iv!(location, count, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void UniformMatrix2(int location, bool transpose, ref float m11)
{
fixed (float* ptr = &m11)
_uniformMatrix2fv!(location, 1, transpose, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void UniformMatrix3(int location, bool transpose, ref float m11)
{
fixed (float* ptr = &m11)
_uniformMatrix3fv!(location, 1, transpose, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void UniformMatrix4(int location, bool transpose, ref float m11)
{
fixed (float* ptr = &m11)
_uniformMatrix4fv!(location, 1, transpose, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void UniformMatrix2(int location, int count, bool transpose, ref float m11)
{
fixed (float* ptr = &m11)
_uniformMatrix2fv!(location, count, transpose, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void UniformMatrix3(int location, int count, bool transpose, ref float m11)
{
fixed (float* ptr = &m11)
_uniformMatrix3fv!(location, count, transpose, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void UniformMatrix4(int location, int count, bool transpose, ref float m11)
{
fixed (float* ptr = &m11)
_uniformMatrix4fv!(location, count, transpose, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void UniformMatrix4(int location, bool transpose, in QMat4 m4)
{
fixed (float* ptr = &m4.M11)
_uniformMatrix4fv!(location, 1, transpose, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void UniformMatrix4(int location, int count, bool transpose, ref QMat4 m4)
{
fixed (float* ptr = &m4.M11)
_uniformMatrix4fv!(location, count, transpose, ptr);
}
}
}
-78
View File
@@ -1,78 +0,0 @@
using System;
using System.Runtime.CompilerServices;
namespace Dashboard.OpenGL
{
public unsafe static partial class GL
{
private delegate void EnableVertexAttribArrayProc(int location);
private delegate void VertexAttribPointerProc(int location, int size, GLEnum type, bool normalized, int stride, IntPtr offset);
private delegate void VertexAttribIPointerProc(int location, int size, GLEnum type, int stride, IntPtr offset);
private static GenObjectsProc? _genVertexArrays;
private static GenObjectsProc? _deleteVertexArrays;
private static BindObjectProc? _bindVertexArray;
private static EnableVertexAttribArrayProc? _enableVertexAttribArray;
private static EnableVertexAttribArrayProc? _disableVertexAttribArray;
private static VertexAttribPointerProc? _vertexAttribPointer;
private static VertexAttribIPointerProc? _vertexAttribIPointer;
private static void LoadVertexArrays()
{
_genVertexArrays = GetProcAddress<GenObjectsProc>("glGenVertexArrays");
_deleteVertexArrays = GetProcAddress<GenObjectsProc>("glDeleteVertexArrays");
_bindVertexArray = GetProcAddress<BindObjectProc>("glBindVertexArray");
_enableVertexAttribArray = GetProcAddress<EnableVertexAttribArrayProc>("glEnableVertexAttribArray");
_disableVertexAttribArray = GetProcAddress<EnableVertexAttribArrayProc>("glDisableVertexAttribArray");
_vertexAttribPointer = GetProcAddress<VertexAttribPointerProc>("glVertexAttribPointer");
_vertexAttribIPointer = GetProcAddress<VertexAttribIPointerProc>("glVertexAttribIPointer");
}
[MethodImpl(AggressiveInlining)]
public static void GenVertexArrays(int count, out int vertexArrays)
{
fixed (int *ptr = &vertexArrays)
_genVertexArrays!(count, ptr);
}
[MethodImpl(AggressiveInlining)]
public static int GenVertexArray()
{
GenVertexArrays(1, out int i);
return i;
}
[MethodImpl(AggressiveInlining)]
public static void GenVertexArrays(int[] vertexArrays) => GenVertexArrays(vertexArrays.Length, out vertexArrays[0]);
[MethodImpl(AggressiveInlining)]
public static void DeleteVertexArrays(int count, ref int vertexArrays)
{
fixed (int *ptr = &vertexArrays)
_deleteVertexArrays!(count, ptr);
}
[MethodImpl(AggressiveInlining)]
public static void DeleteVertexArrays(int[] vertexArrays) => DeleteVertexArrays(vertexArrays.Length, ref vertexArrays[0]);
[MethodImpl(AggressiveInlining)]
public static void DeleteVertexArray(int vertexArray) => DeleteVertexArrays(1, ref vertexArray);
[MethodImpl(AggressiveInlining)]
public static void BindVertexArray(int vertexArray) => _bindVertexArray!(vertexArray);
[MethodImpl(AggressiveInlining)]
public static void EnableVertexAttribArray(int location) => _enableVertexAttribArray!(location);
[MethodImpl(AggressiveInlining)]
public static void DisableVertexAttribArray(int location) => _disableVertexAttribArray!(location);
[MethodImpl(AggressiveInlining)]
public static void VertexAttribPointer(int location, int size, GLEnum type, bool normalized, int stride, int offset) =>
_vertexAttribPointer!(location, size, type, normalized, stride, (IntPtr)offset);
[MethodImpl(AggressiveInlining)]
public static void VertexAttribIPointer(int location, int size, GLEnum type, int stride, int offset) =>
_vertexAttribIPointer!(location, size, type, stride, (IntPtr)offset);
}
}
-128
View File
@@ -1,128 +0,0 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Dashboard.OpenGL
{
public delegate IntPtr GetProcAddressProc(string procName);
public unsafe static partial class GL
{
private delegate void GenObjectsProc(int count, int *ids);
private delegate void BindObjectProc(int id);
private delegate void BindSlottedProc(GLEnum target, int id);
private delegate void GLEnum1Proc(GLEnum x);
private delegate void GLEnum2Proc(GLEnum x, GLEnum y);
private delegate void GLI4Proc(int x, int y, int z, int w);
private delegate void GLF4Proc(float x, float y, float z, float w);
private delegate void DrawElementsProc(GLEnum primitive, int size, GLEnum type, void *offset);
private delegate void DrawArraysProc(GLEnum primitive, int first, int offset);
private delegate void GetIntegervProc(GLEnum pname, int *data);
private delegate void GetFloatvProc(GLEnum pname, float *data);
private delegate byte* GetStringProc(GLEnum pname);
private const short AggressiveInlining = (short)MethodImplOptions.AggressiveInlining;
private static GetProcAddressProc? _getProcAddress;
private static GLEnum1Proc? _enable;
private static GLEnum1Proc? _disable;
private static GLEnum2Proc? _blendFunc;
private static GLEnum1Proc? _depthFunc;
private static GLEnum1Proc? _clear;
private static GLI4Proc? _viewport;
private static GLI4Proc? _scissor;
private static GLF4Proc? _clearColor;
private static DrawElementsProc? _drawElements;
private static DrawArraysProc? _drawArrays;
private static GetIntegervProc? _getIntegerv;
private static GetFloatvProc? _getFloatv;
private static GetStringProc? _getString;
private static T GetProcAddress<T>(string procName)
where T : Delegate
{
IntPtr funcptr = _getProcAddress!(procName);
return Marshal.GetDelegateForFunctionPointer<T>(funcptr);
}
public static void LoadBindings(GetProcAddressProc getProcAddress)
{
_getProcAddress = getProcAddress;
_enable = GetProcAddress<GLEnum1Proc>("glEnable");
_disable = GetProcAddress<GLEnum1Proc>("glDisable");
_blendFunc = GetProcAddress<GLEnum2Proc>("glBlendFunc");
_depthFunc = GetProcAddress<GLEnum1Proc>("glDepthFunc");
_clear = GetProcAddress<GLEnum1Proc>("glClear");
_viewport = GetProcAddress<GLI4Proc>("glViewport");
_scissor = GetProcAddress<GLI4Proc>("glScissor");
_clearColor = GetProcAddress<GLF4Proc>("glClearColor");
_drawElements = GetProcAddress<DrawElementsProc>("glDrawElements");
_drawArrays = GetProcAddress<DrawArraysProc>("glDrawArrays");
_getIntegerv = GetProcAddress<GetIntegervProc>("glGetIntegerv");
_getFloatv = GetProcAddress<GetFloatvProc>("glGetFloatv");
_getString = GetProcAddress<GetStringProc>("glGetString");
LoadBuffer();
LoadProgram();
LoadShader();
LoadTexture();
LoadUniform();
LoadVertexArrays();
}
[MethodImpl(AggressiveInlining)]
public static void Enable(GLEnum cap) => _enable!(cap);
[MethodImpl(AggressiveInlining)]
public static void Disable(GLEnum cap) => _disable!(cap);
[MethodImpl(AggressiveInlining)]
public static void BlendFunc(GLEnum src, GLEnum dst) => _blendFunc!(src, dst);
[MethodImpl(AggressiveInlining)]
public static void DepthFunc(GLEnum func) => _depthFunc!(func);
[MethodImpl(AggressiveInlining)]
public static void Clear(GLEnum buffer_bits) => _clear!(buffer_bits);
[MethodImpl(AggressiveInlining)]
public static void Viewport(int x, int y, int w, int h) => _viewport!(x, y, w, h);
[MethodImpl(AggressiveInlining)]
public static void Scissor(int x, int y, int w, int h) => _scissor!(x, y, w, h);
[MethodImpl(AggressiveInlining)]
public static void ClearColor(float r, float g, float b, float a) => _clearColor!(r, g, b, a);
[MethodImpl(AggressiveInlining)]
public static void DrawElements(GLEnum primitive, int count, GLEnum type, int offset) => _drawElements!(primitive, count, type, (void*)offset);
[MethodImpl(AggressiveInlining)]
public static void DrawArrays(GLEnum primitive, int offset, int count) => _drawArrays!(primitive, offset, count);
[MethodImpl(AggressiveInlining)]
public static void Get(GLEnum pname, out int value)
{
value = default;
fixed(int* ptr = &value)
{
_getIntegerv!(pname, ptr);
}
}
[MethodImpl(AggressiveInlining)]
public static void Get(GLEnum pname, out float value)
{
value = default;
fixed (float* ptr = &value)
{
_getFloatv!(pname, ptr);
}
}
[MethodImpl(AggressiveInlining)]
public static string GetString(GLEnum pname)
{
int length;
byte* str = _getString!(pname);
for (length = 0; str[length] == 0 || length < 256; length++);
return System.Text.Encoding.UTF8.GetString(str, length);
}
}
}
-460
View File
@@ -1,460 +0,0 @@
using System;
using System.IO;
using System.Collections.Generic;
using Dashboard.VertexGenerator;
using static Dashboard.OpenGL.GLEnum;
using Dashboard.Media;
using System.Linq;
using System.Diagnostics;
namespace Dashboard.OpenGL
{
public class GL21Driver : IDisposable
{
private int program;
private int v2Position;
private int fZIndex;
private int v2TexPos;
private int fTexLayer;
private int v4Color;
private int m4Transforms;
private int fMaxZ;
private int iEnableSdf;
private int iEnableTexture;
private int iAlphaDiscard;
private int fSdfThreshold;
private int tx2d;
private int tx2darray;
private bool isDiposed;
private readonly Dictionary<DrawQueue, DrawData> data = new Dictionary<DrawQueue, DrawData>();
private readonly TextureManager textures = new TextureManager();
public bool IsInit { get; private set; } = false;
public event Action<GL21Driver>? OnGCDispose;
public GL21Driver()
{
}
~GL21Driver()
{
Dispose(false);
}
public void Init()
{
if (IsInit) return;
int vs = CreateShader(GL_VERTEX_SHADER, "Dashboard.res.gl21.vert");
int fs = CreateShader(GL_FRAGMENT_SHADER, "Dashboard.res.gl21.frag");
program = GL.CreateProgram();
GL.AttachShader(program, vs);
GL.AttachShader(program, fs);
GL.LinkProgram(program);
if (CheckProgram(program, out string msg) == false)
{
GraphicsException ex = new GraphicsException("Could not link shader program.");
ex.Data.Add("Program Info Log", msg);
}
GL.DeleteShader(vs);
GL.DeleteShader(fs);
v2Position = GL.GetAttribLocation(program, nameof(v2Position));
fZIndex = GL.GetAttribLocation(program, nameof(fZIndex));
v2TexPos = GL.GetAttribLocation(program, nameof(v2TexPos));
fTexLayer = GL.GetAttribLocation(program, nameof(fTexLayer));
v4Color = GL.GetAttribLocation(program, nameof(v4Color));
m4Transforms = GL.GetUniformLocation(program, nameof(m4Transforms));
fMaxZ = GL.GetUniformLocation(program, nameof(fMaxZ));
fSdfThreshold = GL.GetUniformLocation(program, nameof(fSdfThreshold));
iEnableSdf = GL.GetUniformLocation(program, nameof(iEnableSdf));
iEnableTexture = GL.GetUniformLocation(program, nameof(iEnableTexture));
iAlphaDiscard = GL.GetUniformLocation(program, nameof(iAlphaDiscard));
tx2d = GL.GetUniformLocation(program, nameof(tx2d));
tx2darray = GL.GetUniformLocation(program, nameof(tx2darray));
IsInit = true;
}
private void AssertInit()
{
if (!IsInit) throw new InvalidOperationException("Initialize the driver first.");
}
public void Draw(DrawQueue queue, in QRectangle view)
{
AssertInit();
if (!data.TryGetValue(queue, out DrawData? draw))
{
draw = new DrawData(this, queue);
data.Add(queue, draw);
}
// This already binds the vertex array for me.
draw.PrepareFrame();
QVec2 size = view.Size;
QMat4.Orthographic(out QMat4 viewMatrix, view);
GL.Viewport(0, 0, (int)view.Size.X, (int)view.Size.Y);
GL.UseProgram(program);
GL.Uniform1(fMaxZ, (float)(queue.ZDepth+1));
GL.Uniform1(fSdfThreshold, 0.5f);
GL.Uniform1(tx2d, 0);
GL.Enable(GL_BLEND);
GL.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
GL.Enable(GL_SCISSOR_TEST);
GL.Enable(GL_DEPTH_TEST);
GL.DepthFunc(GL_LESS);
foreach (DrawCall call in queue)
{
GL.Scissor(
(int)MathF.Round(call.Bounds.Min.X),
(int)MathF.Round(size.Y - call.Bounds.Max.Y),
(int)MathF.Round(call.Bounds.Size.X),
(int)MathF.Round(call.Bounds.Size.Y));
QMat4.Translation(out QMat4 modelMatrix, call.Bounds.Min.X, call.Bounds.Min.Y, 0);
QMat4 modelView = viewMatrix * modelMatrix;
GL.UniformMatrix4(m4Transforms, false, in modelView);
GL.ActiveTexture(GL_TEXTURE0);
GL.BindTexture(GL_TEXTURE_2D, 0);
if (call.Texture != null)
{
GL.Uniform1(iEnableSdf, call.Texture.IsSdf ? 1 : 0);
GL.Uniform1(iAlphaDiscard, 1);
if (call.Texture.Depth > 1)
{
GL.Uniform1(iEnableTexture, 3);
GL.ActiveTexture(GL_TEXTURE1);
GL.BindTexture(GL_TEXTURE_2D_ARRAY, textures.GetTexture(call.Texture));
}
else
{
GL.Uniform1(iEnableTexture, 2);
GL.ActiveTexture(GL_TEXTURE0);
GL.BindTexture(GL_TEXTURE_2D, textures.GetTexture(call.Texture));
}
}
else
{
GL.Uniform1(iEnableTexture, 0);
}
GL.DrawElements(GL_TRIANGLES, call.Count, GL_UNSIGNED_INT, sizeof(int)*call.Start);
}
GL.Disable(GL_SCISSOR_TEST);
GL.Disable(GL_DEPTH_TEST);
GL.Disable(GL_BLEND);
}
public void ClearDrawQueue(DrawQueue queue)
{
AssertInit();
if (!data.TryGetValue(queue, out DrawData? draw))
return;
draw.Dispose();
data.Remove(queue);
}
private static int CreateShader(GLEnum type, string name)
{
StreamReader source = new StreamReader(typeof(GL21Driver).Assembly.GetManifestResourceStream(name) ?? throw new Exception("Resource not found."));
string text = source.ReadToEnd();
source.Dispose();
int shader = GL.CreateShader(type);
GL.ShaderSource(shader, text);
GL.CompileShader(shader);
if (CheckShader(shader, out string msg) == false)
{
GraphicsException ex = new GraphicsException($"Failed to compile {type} shader stage.");
ex.Data.Add("Shader Stage", type);
ex.Data.Add("Shader Info Log", msg);
ex.Data.Add("Shader Source", text);
throw ex;
}
return shader;
}
private static bool CheckShader(int shader, out string message)
{
message = string.Empty;
GL.GetShader(shader, GL_COMPILE_STATUS, out int i);
if (i != (int)GL_TRUE)
{
message = GL.GetShaderInfoLog(shader);
return false;
}
return true;
}
private static bool CheckProgram(int program, out string message)
{
message = string.Empty;
GL.GetProgram(program, GL_LINK_STATUS, out int i);
if (i != (int)GL_OK)
{
message = GL.GetProgramInfoLog(program);
return false;
}
return true;
}
private void Dispose(bool disposing)
{
if (isDiposed) return;
if (!IsInit)
{
isDiposed = true;
return;
}
if (!disposing)
{
if (OnGCDispose == null)
{
throw new Exception("This object must strictly be disposed from the owning thread, not GC");
}
else
{
OnGCDispose(this);
return;
}
}
GL.DeleteProgram(program);
foreach (DrawData datum in data.Values)
{
datum.Dispose();
}
isDiposed = true;
GC.SuppressFinalize(this);
}
public void Dispose() => Dispose(true);
private class DrawData : IDisposable
{
public DrawQueue Queue { get; }
public int VertexArray { get; }
private readonly GL21Driver driver;
private int vbo1, vbo2;
private int ebo1, ebo2;
public DrawData(GL21Driver driver, DrawQueue queue)
{
Queue = queue;
this.driver = driver;
VertexArray = GL.GenVertexArray();
GL.GenBuffers(1, out vbo1);
GL.GenBuffers(1, out vbo2);
GL.GenBuffers(1, out ebo1);
GL.GenBuffers(1, out ebo2);
isDisposed = false;
}
public void PrepareFrame()
{
int vbo, ebo;
vbo = Swap(ref vbo1, ref vbo2);
ebo = Swap(ref ebo1, ref ebo2);
if (Queue.VertexCount == 0 || Queue.ElementCount == 0)
return;
GL.BindVertexArray(VertexArray);
GL.BindBuffer(GL_ARRAY_BUFFER, vbo);
GL.BufferData(GL_ARRAY_BUFFER, QuikVertex.Stride * Queue.VertexCount, Queue.VertexArray, GL_STREAM_DRAW);
GL.VertexAttribPointer(driver.v2Position, 2, GL_FLOAT, false, QuikVertex.Stride, QuikVertex.PositionOffset);
GL.VertexAttribPointer(driver.fZIndex, 1, GL_UNSIGNED_INT, false, QuikVertex.Stride, QuikVertex.ZIndexOffset);
GL.VertexAttribPointer(driver.v2TexPos, 2, GL_FLOAT, false, QuikVertex.Stride, QuikVertex.TextureCoordinatesOffset);
GL.VertexAttribPointer(driver.fTexLayer, 1, GL_FLOAT, false, QuikVertex.Stride, QuikVertex.TextureLayerOffset);
GL.VertexAttribPointer(driver.v4Color, 4, GL_UNSIGNED_BYTE, true, QuikVertex.Stride, QuikVertex.ColorOffset);
GL.EnableVertexAttribArray(driver.v2Position);
GL.EnableVertexAttribArray(driver.fZIndex);
GL.EnableVertexAttribArray(driver.v2TexPos);
GL.EnableVertexAttribArray(driver.v4Color);
GL.EnableVertexAttribArray(driver.fTexLayer);
GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, Queue.ElementCount * sizeof(int), Queue.ElementArray, GL_STREAM_DRAW);
int Swap(ref int a, ref int b)
{
a ^= b;
b ^= a;
a ^= b;
return a;
}
}
private bool isDisposed;
public void Dispose()
{
if (isDisposed) return;
GL.DeleteVertexArray(VertexArray);
GL.DeleteBuffer(vbo1);
GL.DeleteBuffer(vbo2);
GL.DeleteBuffer(ebo1);
GL.DeleteBuffer(ebo2);
}
}
}
internal class TextureManager : IDisposable
{
private readonly Dictionary<QImage, int> textures = new Dictionary<QImage, int>();
private readonly HashSet<QImage> imagesNotUsed = new HashSet<QImage>();
private bool isDisposed = false;
public void BeginFrame()
{
if (imagesNotUsed.Count > 0)
{
foreach (QImage image in imagesNotUsed)
{
GL.DeleteTexture(textures[image]);
}
imagesNotUsed.Clear();
}
foreach (QImage image in textures.Keys)
{
imagesNotUsed.Add(image);
}
}
public int GetTexture(QImage image)
{
if (textures.TryGetValue(image, out int texture))
{
return texture;
}
if (image.Depth > 1)
{
texture = UploadTexture3d(image);
}
else
{
texture = UploadTexture2d(image);
}
return textures[image] = texture;
}
public int UploadTexture3d(QImage image3d)
{
int texture = GL.GenTexture();
GL.BindTexture(GL_TEXTURE_2D_ARRAY, texture);
image3d.LockBits3d(out QImageLock lck, QImageLockOptions.Default);
GL.TexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, lck.Width, lck.Height, lck.Depth, 0, s_InternalFormat[lck.Format], s_PixelType[lck.Format], lck.ImagePtr);
image3d.UnlockBits();
GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
return texture;
}
public int UploadTexture2d(QImage image2d)
{
int texture = GL.GenTexture();
GL.BindTexture(GL_TEXTURE_2D, texture);
image2d.LockBits2d(out QImageLock lck, QImageLockOptions.Default);
GL.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, lck.Width, lck.Height, 0, s_InternalFormat[lck.Format], s_PixelType[lck.Format], lck.ImagePtr);
image2d.UnlockBits();
GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
switch (image2d.InternalFormat)
{
case QImageFormat.RedU8:
case QImageFormat.RedF:
GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED);
GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED);
GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE);
break;
case QImageFormat.AlphaU8:
case QImageFormat.AlphaF:
GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_ONE);
GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_ONE);
GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_ONE);
GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ALPHA);
break;
}
return texture;
}
public void Dispose()
{
if (isDisposed)
return;
isDisposed = true;
int[] ids = textures.Values.ToArray();
GL.DeleteTextures(ids);
}
private static readonly Dictionary<QImageFormat, GLEnum> s_InternalFormat = new Dictionary<QImageFormat, GLEnum>()
{
[QImageFormat.AlphaF] = GL_ALPHA,
[QImageFormat.AlphaU8] = GL_ALPHA,
[QImageFormat.RedF] = GL_RED,
[QImageFormat.RedU8] = GL_RED,
[QImageFormat.RgbF] = GL_RGB,
[QImageFormat.RgbU8] = GL_RGB,
[QImageFormat.RgbaU8] = GL_RGBA,
[QImageFormat.RgbaF] = GL_RGBA,
};
private static readonly Dictionary<QImageFormat, GLEnum> s_PixelType = new Dictionary<QImageFormat, GLEnum>()
{
[QImageFormat.AlphaF] = GL_FLOAT,
[QImageFormat.RedF] = GL_FLOAT,
[QImageFormat.RgbF] = GL_FLOAT,
[QImageFormat.RgbaF] = GL_FLOAT,
[QImageFormat.AlphaU8] = GL_UNSIGNED_BYTE,
[QImageFormat.RedU8] = GL_UNSIGNED_BYTE,
[QImageFormat.RgbU8] = GL_UNSIGNED_BYTE,
[QImageFormat.RgbaU8] = GL_UNSIGNED_BYTE,
};
}
}
-94
View File
@@ -1,94 +0,0 @@
namespace Dashboard.OpenGL
{
public enum GLEnum : int
{
GL_OK = 0,
GL_TRUE = 1,
GL_FALSE = 0,
GL_ONE = 1,
GL_ZERO = 0,
GL_MAJOR_VERSION = 0x821B,
GL_MINOR_VERSION = 0x821C,
GL_VENDOR = 0x1F00,
GL_RENDERER = 0x1F01,
GL_VERSION = 0x1F02,
GL_EXTENSIONS = 0x1F03,
GL_MAX_TEXTURE_SIZE = 0x0D33,
GL_MAX_3D_TEXTURE_SIZE = 0x8073,
GL_MAX_ARRAY_TEXTURE_LAYERS = 0x88FF,
GL_MULTISAMPLE = 0x809D,
GL_BLEND = 0x0BE2,
GL_COLOR_BUFFER_BIT = 0x00004000,
GL_DEPTH_BUFFER_BIT = 0x00000100,
GL_SRC_ALPHA = 0x0302,
GL_ONE_MINUS_SRC_ALPHA = 0x0303,
GL_VERTEX_SHADER = 0x8B31,
GL_FRAGMENT_SHADER = 0x8B30,
GL_INFO_LOG_LENGTH = 0x8B84,
GL_COMPILE_STATUS = 0x8B81,
GL_LINK_STATUS = 0x8B82,
GL_UNSIGNED_BYTE = 0x1401,
GL_UNSIGNED_SHORT = 0x1403,
GL_UNSIGNED_INT = 0x1405,
GL_FLOAT = 0x1406,
GL_RED = 0x1903,
GL_GREEN = 0x1904,
GL_BLUE = 0x1905,
GL_ALPHA = 0x1906,
GL_RGB = 0x1907,
GL_RGBA = 0x1908,
GL_ARRAY_BUFFER = 0x8892,
GL_ELEMENT_ARRAY_BUFFER = 0x8893,
GL_STREAM_DRAW = 0x88E0,
GL_TEXTURE0 = 0x84C0,
GL_TEXTURE1 = GL_TEXTURE0 + 1,
GL_TEXTURE2 = GL_TEXTURE0 + 2,
GL_TEXTURE3 = GL_TEXTURE0 + 3,
GL_TEXTURE4 = GL_TEXTURE0 + 4,
GL_TEXTURE5 = GL_TEXTURE0 + 5,
GL_TEXTURE6 = GL_TEXTURE0 + 6,
GL_TEXTURE_2D = 0x0DE1,
GL_TEXTURE_2D_ARRAY = 0x8C1A,
GL_UNPACK_ALIGNMENT = 0x0CF5,
GL_TEXTURE_MAG_FILTER = 0x2800,
GL_TEXTURE_MIN_FILTER = 0x2801,
GL_NEAREST = 0x2600,
GL_LINEAR = 0x2601,
GL_NEAREST_MIPMAP_NEAREST = 0x2700,
GL_LINEAR_MIPMAP_NEAREST = 0x2701,
GL_NEAREST_MIPMAP_LINEAR = 0x2702,
GL_LINEAR_MIPMAP_LINEAR = 0x2703,
GL_TEXTURE_WRAP_S = 0x2802,
GL_TEXTURE_WRAP_T = 0x2803,
GL_CLAMP_TO_EDGE = 0x812F,
GL_CLAMP_TO_BORDER = 0x812D,
GL_MIRRORED_REPEAT = 0x8370,
GL_MIRROR_CLAMP_TO_EDGE = 0x8743,
GL_CLAMP = 0x2900,
GL_REPEAT = 0x2901,
GL_TRIANGLES = 0x0004,
GL_SCISSOR_TEST = 0x0C11,
GL_DEPTH_TEST = 0x0B71,
GL_TEXTURE_SWIZZLE_R = 0x8E42,
GL_TEXTURE_SWIZZLE_G = 0x8E43,
GL_TEXTURE_SWIZZLE_B = 0x8E44,
GL_TEXTURE_SWIZZLE_A = 0x8E45,
GL_LESS = 0x0201,
}
}
-46
View File
@@ -1,46 +0,0 @@
using System;
using static Dashboard.OpenGL.GLEnum;
namespace Dashboard.OpenGL
{
[System.Serializable]
public class GraphicsException : System.Exception
{
public GraphicsException()
{
AddExtraData();
}
public GraphicsException(string message) : base(message)
{
AddExtraData();
}
public GraphicsException(string message, System.Exception inner) : base(message, inner)
{
AddExtraData();
}
protected GraphicsException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context)
{
AddExtraData();
}
private void AddExtraData()
{
GL.Get(GL_MAJOR_VERSION, out int major);
GL.Get(GL_MINOR_VERSION, out int minor);
string version = GL.GetString(GL_VERSION);
string vendor = GL.GetString(GL_VENDOR);
string renderer = GL.GetString(GL_RENDERER);
Data.Add("OpenGL Version", new Version(major, minor));
Data.Add("OpenGL Version String", version);
Data.Add("OpenGL Vendor", vendor);
Data.Add("OpenGL Renderer", renderer);
}
}
}
-91
View File
@@ -1,91 +0,0 @@
using Dashboard.CommandMachine;
using Dashboard.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Dashboard.PAL
{
/// <summary>
/// An abstraction layer over the UI input and output.
/// </summary>
public class Dash
{
private readonly IDashHandle handle;
private readonly IDashboardPlatform platform;
public string Title
{
get => platform.PortGetTitle(handle);
set => platform.PortSetTitle(handle, value);
}
public QVec2 Size
{
get => platform.PortGetSize(handle);
set => platform.PortSetSize(handle, value);
}
public QVec2 Position
{
get => platform.PortGetPosition(handle);
set => platform.PortSetPosition(handle, value);
}
public UIBase? UIElement { get; set; }
public bool IsValid => platform.PortIsValid(handle);
public event EventHandler EventRaised
{
add
{
platform.PortSubscribeEvent(handle, value);
}
remove
{
platform.PortUnsubscribeEvent(handle, value);
}
}
public Dash(IDashboardPlatform platform)
{
this.platform = platform;
handle = platform.CreatePort();
}
bool isDisposed = false;
public void Dispose()
{
if (isDisposed) return;
platform.DestroyPort(handle);
isDisposed = true;
}
public void Focus()
{
platform.PortFocus(handle);
}
public void Paint(CommandList? list = null)
{
if (UIElement == null)
return;
list ??= new CommandList();
list.Clear();
UIElement.Bounds = new QRectangle(Size, new QVec2(0,0));
UIElement.Paint(list);
platform.PortPaint(handle, list);
}
public void Show(bool shown = true)
{
platform.PortShow(handle, shown);
}
}
}
-61
View File
@@ -1,61 +0,0 @@
using System;
using Dashboard.CommandMachine;
using Dashboard.Media;
namespace Dashboard.PAL
{
/// <summary>
/// An empty interface to statically type Quik port handles.
/// </summary>
public interface IDashHandle
{
}
/// <summary>
/// The primary primary platform abstraction interface for dashboard hosts.
/// </summary>
public interface IDashboardPlatform : IDisposable
{
/// <summary>
/// The title of the application.
/// </summary>
string? Title { get; set; }
/// <summary>
/// The default icon for the application.
/// </summary>
QImage? Icon { get; set; }
/// <summary>
/// The event raised when an event is received.
/// </summary>
event EventHandler? EventRaised;
/// <summary>
/// Raise the events that have been enqueued.
/// </summary>
/// <param name="block">True to block until a new event arrives.</param>
void ProcessEvents(bool block);
/// <summary>
/// Create a window.
/// </summary>
/// <returns>The window instance.</returns>
IDashHandle CreatePort();
void DestroyPort(IDashHandle port);
string PortGetTitle(IDashHandle port);
void PortSetTitle(IDashHandle port, string title);
QVec2 PortGetSize(IDashHandle port);
void PortSetSize(IDashHandle port, QVec2 size);
QVec2 PortGetPosition(IDashHandle port);
void PortSetPosition(IDashHandle port, QVec2 position);
bool PortIsValid(IDashHandle port);
void PortSubscribeEvent(IDashHandle port, EventHandler handler);
void PortUnsubscribeEvent(IDashHandle port, EventHandler handler);
void PortFocus(IDashHandle port);
void PortShow(IDashHandle port, bool shown = true);
void PortPaint(IDashHandle port, CommandList commands);
void GetMaximumImage(out int width, out int height);
void GetMaximumImage(out int width, out int height, out int depth);
}
}
-67
View File
@@ -1,67 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using Dashboard.Media.Font;
namespace Dashboard.PAL
{
/// <summary>
/// Flags that effect font search criterea.
/// </summary>
[Flags]
public enum FontMatchCriteria
{
None = 0,
Family = 1 << 0,
Slant = 1 << 1,
Weight = 1 << 2,
Stretch = 1 << 3,
All = Family | Slant | Weight | Stretch,
}
/// <summary>
/// An abstraction over the system font database.
/// </summary>
public interface IFontDataBase
{
/// <summary>
/// All the fonts installed in the system.
/// </summary>
IEnumerable<FontFace> All { get; }
public FontFace Serif => GetSystemFontFace(SystemFontFamily.Serif);
public FontFace Sans => GetSystemFontFace(SystemFontFamily.Sans);
public FontFace Monospace => GetSystemFontFace(SystemFontFamily.Monospace);
public FontFace Cursive => GetSystemFontFace(SystemFontFamily.Cursive);
public FontFace Fantasy => GetSystemFontFace(SystemFontFamily.Fantasy);
/// <summary>
/// Search for the given font face.
/// </summary>
/// <param name="prototype">The font face prototype.</param>
/// <param name="criteria">The match criteria</param>
/// <returns>A list of fonts sorted by the closest match first.</returns>
IEnumerable<FontFace> Search(FontFace prototype, FontMatchCriteria criteria = FontMatchCriteria.All);
/// <summary>
/// Get the font face file info if it exists.
/// </summary>
/// <param name="face">The face to look for.</param>
/// <returns>The file info if it exists.</returns>
FileInfo FontFileInfo(FontFace face);
/// <summary>
/// Open a font face.
/// </summary>
/// <param name="face">The font face to open.</param>
/// <returns>The stream to the font face.</returns>
Stream Open(FontFace face);
/// <summary>
/// Get a system font family.
/// </summary>
/// <param name="family">The family type to look up.</param>
/// <returns>The name of a font in this family.</returns>
FontFace GetSystemFontFace(SystemFontFamily family);
}
}
-12
View File
@@ -1,12 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Dashboard.Media;
namespace Dashboard.PAL
{
public interface IFontFactory
{
bool TryOpen(Stream stream, [NotNullWhen(true)] out QFont font);
}
}
-133
View File
@@ -1,133 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using Dashboard.CommandMachine;
using Dashboard.Controls;
using Dashboard.Media;
using Dashboard.PAL;
using Dashboard.Typography;
namespace Dashboard
{
/// <summary>
/// Main class for Quik applications.
/// </summary>
public class DashboardApplication
{
/// <summary>
/// The application platform driver.
/// </summary>
public IDashboardPlatform Platform { get; }
/// <summary>
/// Title of the application.
/// </summary>
public string? Title
{
get => Platform.Title;
set => Platform.Title = value;
}
/// <summary>
/// Application icon.
/// </summary>
public QImage? Icon
{
get => Platform.Icon;
set => Platform.Icon = value;
}
public PAL.Dash? MainPort { get; private set; } = null;
public FontProvider FontProvider { get; }
/// <summary>
/// List of media loaders, drivers that load media such as images and fonts.
/// </summary>
public List<MediaLoader> MediaLoaders { get; } = new List<MediaLoader>();
public DashboardApplication(IDashboardPlatform platform)
{
Platform = platform;
FontProvider = new FontProvider(this);
Current = this;
}
public IDisposable? GetMedia(object key, MediaHint hint)
{
IDisposable? disposable = null;
foreach (MediaLoader loader in MediaLoaders)
{
disposable = loader.GetMedia(key, hint);
if (disposable != null)
break;
}
return disposable;
}
public IDisposable? GetMedia<T>(T key, MediaHint hint)
{
IDisposable? disposable = null;
foreach (MediaLoader loader in MediaLoaders)
{
if (loader is MediaLoader<T> typedLoader)
{
disposable = typedLoader.GetMedia(key, hint);
if (disposable != null)
break;
}
}
return disposable;
}
private CommandList cmd = new CommandList();
public void Run(View mainView, bool yield = true)
{
Init(mainView);
while (RunSync())
{
if (yield)
{
Thread.Yield();
}
}
}
public void Init(View mainView)
{
MainPort = new PAL.Dash(Platform) { UIElement = mainView };
MainPort.EventRaised += (sender, ea) => mainView.NotifyEvent(sender, ea);
}
public bool RunSync()
{
if (!MainPort!.IsValid)
return false;
Platform.ProcessEvents(false);
if (MainPort.IsValid)
{
cmd.Clear();
MainPort.Paint(cmd);
}
return true;
}
public static DashboardApplication Current { get; private set; } = null!;
public static void SetCurrentApplication(DashboardApplication application)
{
Current = application;
}
}
}
-548
View File
@@ -1,548 +0,0 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
namespace Dashboard
{
/// <summary>
/// A 2 dimensional Vector.
/// </summary>
[DebuggerDisplay("({X}, {Y})")]
public struct QVec2
{
public float X;
public float Y;
public float Magnitude => MathF.Sqrt(X * X + Y * Y);
public QVec2(float x, float y)
{
X = x;
Y = y;
}
public QVec2 Normalize() => this * (1.0f / Magnitude);
public float Atan2() => MathF.Atan2(Y, X);
public static QVec2 operator +(QVec2 a, QVec2 b)
{
return new QVec2()
{
X = a.X + b.X,
Y = a.Y + b.Y
};
}
public static QVec2 operator -(QVec2 a)
{
return new QVec2()
{
X = -a.X,
Y = -a.Y
};
}
public static QVec2 operator -(QVec2 a, QVec2 b)
{
return new QVec2()
{
X = a.X - b.X,
Y = a.Y - b.Y
};
}
public static QVec2 operator *(float a, QVec2 b)
{
return new QVec2()
{
X = a * b.X,
Y = a * b.Y
};
}
public static QVec2 operator *(QVec2 a, float b) => b * a;
public static bool operator ==(QVec2 a, QVec2 b) => a.X == b.X && a.Y == b.Y;
public static bool operator !=(QVec2 a, QVec2 b) => a.X != b.X || a.Y != b.Y;
public override bool Equals(object? obj)
{
if (obj is QVec2)
{
return (QVec2) obj == this;
}
else
{
return false;
}
}
public override int GetHashCode()
{
return 63671 * X.GetHashCode() ^ 81083 * Y.GetHashCode();
}
public static float Dot(QVec2 a, QVec2 b)
{
return a.X * b.X + a.Y * b.Y;
}
public override string ToString()
{
return $"({X}; {Y})";
}
public static readonly QVec2 Zero = new QVec2(0, 0);
public static readonly QVec2 UnitX = new QVec2(1, 0);
public static readonly QVec2 UnitY = new QVec2(0, 1);
}
/// <summary>
/// A RGBA color value.
/// </summary>
[DebuggerDisplay("({R}, {G}, {B}, {A})")]
public struct QColor
{
/// <summary>
/// Red channel.
/// </summary>
public byte R;
/// <summary>
/// Green channel.
/// </summary>
public byte G;
/// <summary>
/// Blue channel.
/// </summary>
public byte B;
/// <summary>
/// Alpha channel.
/// </summary>
public byte A;
public QColor(byte r, byte g, byte b, byte a)
{
R = r;
G = g;
B = b;
A = a;
}
public QColor(byte r, byte g, byte b) : this(r, g, b, 1) { }
public QColor(uint hexCode)
{
R = (byte)((hexCode >> 24) & 0xFF);
G = (byte)((hexCode >> 16) & 0xFF);
B = (byte)((hexCode >> 8 ) & 0xFF);
A = (byte)((hexCode >> 0 ) & 0xFF);
}
public QColor(int hexCode) : this((uint)hexCode) { }
public static readonly QColor Black = new QColor(0, 0, 0, 255);
public static readonly QColor Red = new QColor(255, 0, 0, 255);
public static readonly QColor Green = new QColor(0, 255, 0, 255);
public static readonly QColor Blue = new QColor(0, 0, 255, 255);
public static readonly QColor Yellow = new QColor(255, 255, 0, 255);
public static readonly QColor Cyan = new QColor(0, 255, 255, 255);
public static readonly QColor Magenta = new QColor(255, 0, 255, 255);
public static readonly QColor White = new QColor(255, 255, 255, 255);
public static explicit operator QColorF(QColor a)
{
return new QColorF(a.R/255.0f, a.G/255.0f, a.B/255.0f, a.A/255.0f);
}
}
public struct QColorF
{
/// <summary>
/// Red channel.
/// </summary>
public float R;
/// <summary>
/// Green channel.
/// </summary>
public float G;
/// <summary>
/// Blue channel.
/// </summary>
public float B;
/// <summary>
/// Alpha channel.
/// </summary>
public float A;
public QColorF(float r, float g, float b, float a)
{
R = r; G = g; B = b; A = a;
}
public QColorF(float r, float g, float b) : this(r, g, b, 1.0f) { }
public QColorF(uint hexCode)
{
R = ((hexCode >> 24) & 0xFF)/255.0f;
G = ((hexCode >> 16) & 0xFF)/255.0f;
B = ((hexCode >> 8 ) & 0xFF)/255.0f;
A = ((hexCode >> 0 ) & 0xFF)/255.0f;
}
public QColorF(int hexCode) : this((uint)hexCode) { }
public static readonly QColorF Black = new QColorF(0, 0, 0, 1.0f);
public static readonly QColorF Red = new QColorF(1.0f, 0, 0, 1.0f);
public static readonly QColorF Green = new QColorF(0, 1, 0, 1);
public static readonly QColorF Blue = new QColorF(0, 0, 1, 1);
public static readonly QColorF Yellow = new QColorF(1, 1, 0, 1);
public static readonly QColorF Cyan = new QColorF(0, 1, 1, 1);
public static readonly QColorF Magenta = new QColorF(1, 0, 1, 1);
public static readonly QColorF White = new QColorF(1, 1, 1, 1);
public static explicit operator QColor(QColorF a)
{
return new QColor((byte)(a.R * 255), (byte)(a.G * 255), (byte)(a.B * 255), (byte)(a.A * 255));
}
}
/// <summary>
/// A bezier curve segment.
/// </summary>
[DebuggerDisplay("{Start} -- {ControlA} -- {ControlB} -- {End}")]
public struct QBezier
{
/// <summary>
/// Segment start point.
/// </summary>
public QVec2 Start;
/// <summary>
/// Start point control point.
/// </summary>
public QVec2 ControlA;
/// <summary>
/// End point control point.
/// </summary>
public QVec2 ControlB;
/// <summary>
/// Segment end point.
/// </summary>
public QVec2 End;
/// <summary>
/// An approximation of the arc length of the bezier curve, for calculating rasterization resolution.
/// </summary>
public float RasterizationArc =>
0.5f * (End - Start).Magnitude +
0.5f * ((ControlA - Start).Magnitude + (ControlB - ControlA).Magnitude + (End - ControlB).Magnitude);
public QBezier(QVec2 start, QVec2 controlA, QVec2 controlB, QVec2 end)
{
Start = start;
ControlA = controlA;
ControlB = controlB;
End = end;
}
public QBezier(
float startX,
float startY,
float controlAx,
float controlAy,
float controlBx,
float controlBy,
float endX,
float endY)
: this(
new QVec2(startX, startY),
new QVec2(controlAx, controlAy),
new QVec2(controlBx, controlBy),
new QVec2(endX, endY))
{
}
/// <summary>
/// Get a point in the curve segment.
/// </summary>
/// <param name="t">Control parameter (between 0 and 1)</param>
/// <returns>The point on the curve.</returns>
public QVec2 GetBezierPoint(float t)
{
float T = 1 - t;
return
T * T * T * Start +
3 * T * T * t * ControlA +
3 * T * t * t * ControlB +
t * t * t * End;
}
/// <summary>
/// Get the tangent on the curve.
/// </summary>
/// <param name="t">Control parameter (between 0 and 1)</param>
/// <returns>The tangent curve.</returns>
public QVec2 GetBezierTangent(float t)
{
float T = 1 - t;
return
(
3 * T * T * (ControlA - Start) +
6 * T * t * (ControlB - ControlA) +
3 * t * t * (End - ControlB)
).Normalize();
}
internal QVec2 GetBezierNormal(float t)
{
QVec2 tangent = GetBezierTangent(t);
return new QVec2(-tangent.Y, tangent.X);
}
}
/// <summary>
/// A line segment.
/// </summary>
[DebuggerDisplay("{Start} -- {End}")]
public struct QLine
{
/// <summary>
/// Start point.
/// </summary>
public QVec2 Start;
/// <summary>
/// End point.
/// </summary>
public QVec2 End;
public QLine(QVec2 start, QVec2 end)
{
Start = start;
End = end;
}
public QLine(float startX, float startY, float endX, float endY)
{
Start.X = startX;
Start.Y = startY;
End.X = endX;
End.Y = endY;
}
public QVec2 Normal()
{
QVec2 tangent = Tangent();
return new QVec2(-tangent.Y, tangent.X);
}
public QVec2 Tangent()
{
return (End - Start).Normalize();
}
}
/// <summary>
/// A rectangle.
/// </summary>
[DebuggerDisplay("({Left}, {Top}, {Right}, {Bottom})")]
public struct QRectangle
{
/// <summary>
/// Position maximum point.
/// </summary>
public QVec2 Max;
/// <summary>
/// Position minimum point.
/// </summary>
public QVec2 Min;
public float Left
{
get => Min.X;
set => Min.X = value;
}
public float Right
{
get => Max.X;
set => Max.X = value;
}
public float Top
{
get => Min.Y;
set => Min.Y = value;
}
public float Bottom
{
get => Max.Y;
set => Max.Y = value;
}
public QVec2 Size
{
get => Max - Min;
set => Max = Min + value;
}
public QRectangle(QVec2 max, QVec2 min)
{
Max = max;
Min = min;
}
public QRectangle(float r, float b, float l, float t)
{
Max = new QVec2() {X = r, Y = b};
Min = new QVec2() {X = l, Y = t};
}
public bool Contains(QVec2 point)
{
return
point.X > Left && point.X < Right &&
point.Y > Bottom && point.Y < Top;
}
internal void Translate(in QVec2 offset)
{
Min += offset;
Max += offset;
}
public static QRectangle Intersect(in QRectangle a, in QRectangle b) =>
new QRectangle(
Math.Max(a.Right, b.Right),
Math.Max(a.Bottom, b.Bottom)
,
Math.Min(a.Left, b.Left),
Math.Min(a.Top, b.Top));
}
/// <summary>
/// An ellipse.
/// </summary>
/// <remarks>It is undefined to have an ellipse with non-orthogonal axes.</remarks>
[DebuggerDisplay("{Center} ellipse {AxisA}; {AxisB}")]
public struct QEllipse
{
/// <summary>
/// Ellipse center point.
/// </summary>
public QVec2 Center;
/// <summary>
/// First ellipse axis.
/// </summary>
public QVec2 AxisA;
/// <summary>
/// Second ellipse axis.
/// </summary>
public QVec2 AxisB;
}
/// <summary>
/// A triangle.
/// </summary>
[DebuggerDisplay("{A} -- {B} -- {C}")]
public struct QTriangle
{
/// <summary>
/// First vertex.
/// </summary>
public QVec2 A;
/// <summary>
/// Second vertex.
/// </summary>
public QVec2 B;
/// <summary>
/// Third vertex.
/// </summary>
public QVec2 C;
}
[DebuggerDisplay("[{M11} {M12} {M13} {M14}; {M21} {M22} {M23} {M24}; {M31} {M32} {M33} {M34}; {M41} {M42} {M43} {M44}]")]
public struct QMat4
{
public float M11, M21, M31, M41;
public float M12, M22, M32, M42;
public float M13, M23, M33, M43;
public float M14, M24, M34, M44;
public static QMat4 Identity { get; } = new QMat4()
{
M11 = 1.0f,
M22 = 1.0f,
M33 = 1.0f,
M44 = 1.0f
};
public static void Translation(out QMat4 mat, float x, float y, float z)
{
mat = Identity;
mat.M14 = x;
mat.M24 = y;
mat.M34 = z;
}
public static void Scale(out QMat4 mat, float x, float y, float z)
{
mat = default;
mat.M11 = x;
mat.M22 = y;
mat.M33 = z;
mat.M44 = 1.0f;
}
public static void Orthographic(out QMat4 mat, QRectangle bounds, float near = 1, float far = -1)
{
float a, b, c;
mat = Identity;
a = 1.0f/(bounds.Right - bounds.Left);
b = 1.0f/(bounds.Top - bounds.Bottom);
c = 1.0f/(far - near);
mat.M11 = 2 * a;
mat.M22 = 2 * b;
mat.M33 = -2 * c;
mat.M14 = -a * (bounds.Left + bounds.Right);
mat.M24 = -b * (bounds.Top + bounds.Bottom);
mat.M34 = -c * (far + near);
mat.M44 = 1.0f;
}
public static QMat4 operator *(in QMat4 a, in QMat4 b)
{
QMat4 mat4 = default;
mat4.M11 = a.M11 * b.M11 + a.M12 * b.M21 + a.M13 * b.M31 + a.M14 * b.M41;
mat4.M12 = a.M11 * b.M12 + a.M12 * b.M22 + a.M13 * b.M32 + a.M14 * b.M42;
mat4.M13 = a.M11 * b.M13 + a.M12 * b.M23 + a.M13 * b.M33 + a.M14 * b.M43;
mat4.M14 = a.M11 * b.M14 + a.M12 * b.M24 + a.M13 * b.M34 + a.M14 * b.M44;
mat4.M21 = a.M21 * b.M11 + a.M22 * b.M21 + a.M23 * b.M31 + a.M24 * b.M41;
mat4.M22 = a.M21 * b.M12 + a.M22 * b.M22 + a.M23 * b.M32 + a.M24 * b.M42;
mat4.M23 = a.M21 * b.M13 + a.M22 * b.M23 + a.M23 * b.M33 + a.M24 * b.M43;
mat4.M24 = a.M21 * b.M14 + a.M22 * b.M24 + a.M23 * b.M34 + a.M24 * b.M44;
mat4.M31 = a.M31 * b.M11 + a.M32 * b.M21 + a.M33 * b.M31 + a.M34 * b.M41;
mat4.M32 = a.M31 * b.M12 + a.M32 * b.M22 + a.M33 * b.M32 + a.M34 * b.M42;
mat4.M33 = a.M31 * b.M13 + a.M32 * b.M23 + a.M33 * b.M33 + a.M34 * b.M43;
mat4.M34 = a.M31 * b.M14 + a.M32 * b.M24 + a.M33 * b.M34 + a.M34 * b.M44;
mat4.M41 = a.M41 * b.M11 + a.M42 * b.M21 + a.M43 * b.M31 + a.M44 * b.M41;
mat4.M42 = a.M41 * b.M12 + a.M42 * b.M22 + a.M43 * b.M32 + a.M44 * b.M42;
mat4.M43 = a.M41 * b.M13 + a.M42 * b.M23 + a.M43 * b.M33 + a.M44 * b.M43;
mat4.M44 = a.M41 * b.M14 + a.M42 * b.M24 + a.M43 * b.M34 + a.M44 * b.M44;
return mat4;
}
}
}
-275
View File
@@ -1,275 +0,0 @@
using System;
using System.Collections.Generic;
using Dashboard.Media;
using Dashboard.Media.Font;
namespace Dashboard
{
public enum TextAlignment
{
Left,
Center,
Right,
Justify
}
public enum TextDecoration
{
None,
Underline,
Overline,
Strikethrough
}
public enum TextTransform
{
None,
Title,
Upper,
Lower,
}
public enum ListMarkerType
{
None,
Disc,
Circle,
FilledSquare,
Square,
Dash,
ArabicNumeral,
RomanNumeralUpper,
RomanNumeralLower,
AlphabetUpper,
AlphabetLower,
Image
}
public enum ListMarkerPosition
{
Inside,
Outside,
}
public abstract class StyleBase
{
public abstract object? this[string key] { get; set; }
public QColor? Color
{
get => (QColor?)this["color"];
set => this["color"] = value;
}
public float? LineHeight
{
get => (float?)this["line-height"];
set => this["line-height"] = value;
}
public float? LetterSpacing
{
get => (float?)this["letter-spacing"];
set => this["letter-spacing"] = value;
}
public TextAlignment? TextAlignment
{
get => (TextAlignment?)this["text-alignment"];
set => this["text-alignment"] = value;
}
public TextDecoration? TextDecoration
{
get => (TextDecoration?)this["text-decoration"];
set => this["text-decoration"] = value;
}
public float? TextIndent
{
get => (float?)this["text-indent"];
set => this["text-indent"] = value;
}
public TextTransform? TextTransform
{
get => (TextTransform?)this["text-transform"];
set => this["text-transform"] = value;
}
public ListMarkerType? ListMarker
{
get => (ListMarkerType?)this["list-marker"];
set => this["list-marker"] = value;
}
public ListMarkerPosition? ListMarkerPosition
{
get => (ListMarkerPosition?)this["list-marker-position"];
set => this["list-marker-position"] = value;
}
public QImage? ListMarkerImage
{
get => (QImage?)this["list-marker-image"];
set => this["list-marker-image"] = value;
}
public float? StrokeWidth
{
get => (float?)this["stroke-width"];
set => this["stroke-width"] = value;
}
public QColor? StrokeColor
{
get => (QColor?)this["stroke-color"];
set => this["stroke-color"] = value;
}
public FontFace? Font
{
get => (FontFace?)this["font"];
set => this["font"] = value;
}
public int? ZIndex
{
get => (int?)this["z-index"];
set => this["z-index"] = value;
}
}
public class Style : StyleBase
{
private readonly Dictionary<string, object> _keys = new Dictionary<string, object>();
public override object? this[string styleKey]
{
get => _keys.TryGetValue(styleKey, out object? value) ? value : null;
set
{
if (value == null)
_keys.Remove(styleKey);
else
_keys[styleKey] = value;
}
}
}
public class StyleStack : StyleBase
{
public readonly List<Style> _styles = new List<Style>();
public Style BaseStyle { get; }
public override object? this[string key]
{
get
{
object? value = null;
for (int i = _styles.Count; i != 0 && value == null; i--)
{
value = _styles[i-1][key];
}
return value;
}
set => throw new InvalidOperationException();
}
public StyleStack(Style baseStyle)
{
BaseStyle = baseStyle;
Push(BaseStyle);
}
public void Clear()
{
_styles.Clear();
Push(BaseStyle);
}
public void Push(Style style)
{
_styles.Add(style);
}
public void Pop()
{
if (_styles.Count == 1)
return;
_styles.RemoveAt(_styles.Count - 1);
}
}
/// <summary>
/// A line stipple pattern.
/// </summary>
public struct QuikStipplePattern
{
/// <summary>
/// The stipple pitch value.
/// </summary>
public float Pitch;
/// <summary>
/// The stipple duty cycle.
/// </summary>
public float DutyCycle;
public QuikStipplePattern(float pitch, float dutyCycle)
{
Pitch = pitch;
DutyCycle = dutyCycle;
}
public static QuikStipplePattern None => new QuikStipplePattern(0.0f, 1.0f);
}
/// <summary>
/// Stroke style for lines and borders.
/// </summary>
public class QuikStrokeStyle
{
/// <summary>
/// Stroke color.
/// </summary>
public QColor Color { get; set; }
/// <summary>
/// Stroke width.
/// </summary>
public float Width { get; set; }
// /// <summary>
// /// Stroke stipple pattern.
// /// </summary>
// public QuikStipplePattern StipplePattern { get; set; }
public QuikStrokeStyle()
{
}
public QuikStrokeStyle(QColor color, float width /*, QuikStipplePattern pattern*/)
{
Color = color;
Width = width;
// StipplePattern = pattern;
}
// public QuikStrokeStyle(QuikColor color, float width) : this(color, width, QuikStipplePattern.None)
// {
// }
}
/// <summary>
/// Fill style for rectangles and the like.
/// </summary>
public class QuikFillStyle
{
public QColor Color { get; set; }
}
}
-108
View File
@@ -1,108 +0,0 @@
using Dashboard.Media;
using Dashboard.Media.Font;
using Dashboard.PAL;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
namespace Dashboard.Typography
{
/// <summary>
/// The font provider is a caching object that provides fonts for typesetting classes.
/// </summary>
public class FontProvider : IDisposable
{
private Dictionary<FontFace, QFont> Fonts { get; } = new Dictionary<FontFace, QFont>();
private HashSet<QFont> UsedFonts { get; } = new HashSet<QFont>();
public readonly FontRasterizerOptions RasterizerOptions;
public IFontDataBase? Database { get; set; }
public IFontFactory? FontFactory { get; set; }
private readonly DashboardApplication App;
public QFont this[FontFace info]
{
get
{
if (!Fonts.TryGetValue(info, out QFont? font))
{
using Stream str = Database?.Open(info) ?? throw new Exception("Font could not be found.");
if (FontFactory?.TryOpen(str, out font) ?? false)
{
Fonts.Add(info, font);
}
else
{
throw new Exception("Font not found.");
}
}
UsedFonts.Add(font);
return font;
}
}
public QFont this[SystemFontFamily family]
{
get
{
return this[Database?.GetSystemFontFace(family) ?? throw new Exception("No font database.")];
}
}
public FontProvider(DashboardApplication app, in FontRasterizerOptions options)
{
RasterizerOptions = options;
App = app;
Type? fdb = Type.GetType("Quik.Media.Defaults.FontDataBaseProvider, Quik.Media.Defaults");
if (fdb != null)
{
PropertyInfo? instanceProperty = fdb.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.GetProperty);
if (instanceProperty != null)
{
Database = (IFontDataBase)instanceProperty.GetValue(null)!;
}
}
Type? ffact = Type.GetType("Quik.Media.Defaults.FreeTypeFontFactory, Quik.Media.Defaults");
if (ffact != null)
{
ConstructorInfo? ctor = ffact.GetConstructor(Array.Empty<Type>());
FontFactory = (IFontFactory?)ctor?.Invoke(null);
}
}
public FontProvider(DashboardApplication app)
: this(app, FontRasterizerOptions.Default)
{
}
/// <summary>
/// Tracks the use of fonts used by this typesetter and removes any that haven't been referenced since the last cycle.
/// </summary>
public void Collect()
{
// foreach (FontJar jar in Fonts.Values.ToArray())
// {
// if (!UsedFonts.Contains(jar))
// {
// Fonts.Remove(jar.Info);
// }
// }
// UsedFonts.Clear();
}
private bool isDisposed = false;
public void Dispose()
{
if (isDisposed) return;
isDisposed = true;
foreach (QFont font in Fonts.Values)
{
font.Dispose();
}
}
}
}
-509
View File
@@ -1,509 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Dashboard.Media;
namespace Dashboard.Typography
{
/// <summary>
/// An atomic horizontal block of text which cannot be further divided.
/// </summary>
public struct HorizontalTextBlock
{
/// <summary>
/// The font associated with the text block.
/// </summary>
/// <value></value>
// public QuikFont Font { get; }
/// <summary>
/// Textual contents of the text block.
/// </summary>
public string Text { get; }
/// <summary>
/// Indicates this text block should be layed out right to left.
/// </summary>
public bool IsRTL { get; }
/// <summary>
/// Indicates this is a whitespace block.
/// </summary>
public bool IsWhitespace => string.IsNullOrWhiteSpace(Text);
public float Width { get; }
public float Ascend { get; }
public float Descend { get; }
public float Height => Ascend - Descend;
public HorizontalTextBlock(object font, string text, bool rtl = false)
{
// Font = font;
Text = text;
IsRTL = rtl;
float width = 0.0f;
float ascend = 0.0f;
float descend = 0.0f;
foreach (char chr in text)
{
// font.GetCharacter(chr, out _, out QGlyphMetrics glyph);
// width += glyph.Advance.X;
// ascend = Math.Max(ascend, glyph.HorizontalBearing.Y);
// descend = Math.Min(descend, glyph.HorizontalBearing.Y - glyph.Size.Y);
}
Width = width;
Ascend = ascend;
Descend = descend;
}
public HorizontalTextBlock(float width)
{
// Font = null;
Text = string.Empty;
IsRTL = false;
Width = width;
Ascend = Descend = 0.0f;
}
}
/// <summary>
/// An atomic vertical block of text which cannot be further divided.
/// </summary>
public struct VerticalTextBlock
{
// public QuikFont Font { get; }
public string Text { get; }
public bool IsWhitespace => string.IsNullOrWhiteSpace(Text);
public float Width { get; }
public float Height { get; }
public VerticalTextBlock(object font, string text)
{
// Font = font;
Text = text;
float width = 0.0f;
float height = 0.0f;
foreach(char chr in text)
{
// font.GetCharacter(chr, out _, out QGlyphMetrics glyph);
// width = Math.Max(width, - glyph.VerticalBearing.X * 2);
// height += glyph.Advance.Y;
}
Width = width;
Height = height;
}
public VerticalTextBlock(float height)
{
// Font = null;
Text = string.Empty;
Width = 0.0f;
Height = height;
}
}
public abstract class Paragraph
{
public abstract bool IsVertical { get; }
public float JustifyLimit { get; set; } = 30.0f;
public TextAlignment Alignment { get; set; } = TextAlignment.Default;
public float PreSpace { get; set; } = 0.0f;
public float PostSpace { get; set; } = 0.0f;
public float FirstLineInset { get; set; } = 0.0f;
public float LineGap { get; set; } = 12.0f;
public abstract void Typeset(TypesetGroup group, float width);
protected abstract void AppendBlock(object font, string text, bool rtl = false);
public void ConsumeText(object font, string text)
{
StringBuilder segment = new StringBuilder();
bool rtl = false;
bool ws = false;
foreach(char chr in text)
{
UnicodeCategory cat = char.GetUnicodeCategory(chr);
// FIXME: don't ignore control characters like a barbarian.
// TODO: how do I detect text flow direction???
if (char.IsWhiteSpace(chr) && chr != '\u00a0')
{
if (ws)
{
segment.Append(chr);
}
else
{
AppendBlock(font, segment.ToString());
segment.Clear();
segment.Append(chr);
ws = true;
}
}
else
{
if (ws)
{
AppendBlock(font, segment.ToString(), rtl);
segment.Clear();
segment.Append(chr);
ws = false;
}
else
{
segment.Append(chr);
}
}
}
if (segment.Length > 0)
{
AppendBlock(font, segment.ToString(), rtl);
}
}
}
public class HorizontalParagraph : Paragraph
{
public override bool IsVertical => false;
public List<HorizontalTextBlock> Blocks { get; } = new List<HorizontalTextBlock>();
public override void Typeset(TypesetGroup group, float width)
{
Queue<HorizontalTextBlock> line = new Queue<HorizontalTextBlock>();
int index = 0;
bool firstLine = true;
QVec2 pen = new QVec2(0, -PreSpace);
while (index < Blocks.Count)
{
index
+= GatherLine(
index,
width - (firstLine ? FirstLineInset : 0),
line,
out float excess,
out float ascend,
out float descend);
firstLine = false;
pen.Y -= ascend;
float interblockWs =
Alignment.HasFlag(TextAlignment.Justify) && excess < JustifyLimit
? excess / (line.Count - 1)
: 0.0f;
switch (Alignment & TextAlignment.HorizontalMask)
{
default:
case TextAlignment.AlignLeft:
if (firstLine) pen.X += FirstLineInset;
break;
case TextAlignment.AlignCenterH:
pen.X += excess / 2;
break;
case TextAlignment.AlignRight:
pen.X += excess;
break;
}
PutBlock(group, line, interblockWs, ref pen);
pen.Y -= LineGap - descend;
pen.X = 0.0f;
}
pen.Y -= PostSpace;
group.BoundingBox = new QRectangle(width, pen.Y, 0, 0);
group.Translate(-pen);
}
private int GatherLine(
int index,
float width,
Queue<HorizontalTextBlock> line,
out float excess,
out float ascend,
out float descend)
{
float currentWidth = 0.0f;
ascend = descend = 0.0f;
for (int i = index; i < Blocks.Count; i++)
{
HorizontalTextBlock block = Blocks[i];
if (currentWidth + block.Width > width)
{
break;
}
ascend = Math.Max(ascend, block.Ascend);
descend = Math.Min(descend, block.Descend);
currentWidth += block.Width;
line.Enqueue(block);
}
excess = width - currentWidth;
return line.Count;
}
public void PutBlock(
TypesetGroup group,
Queue<HorizontalTextBlock> line,
float interblockWs,
ref QVec2 pen)
{
QVec2 penpal = pen;
while (line.TryDequeue(out HorizontalTextBlock block))
{
if (block.IsWhitespace)
{
penpal.X += block.Width + interblockWs;
continue;
}
if (block.IsRTL)
{
for (int i = block.Text.Length - 1; i >= 0; i--)
{
char chr = block.Text[i];
// block.Font.GetCharacter(chr, out QuikTexture texture, out QGlyphMetrics metrics);
// group.Add(
// new TypesetCharacter(
// chr,
// texture,
// new QRectangle(
// penpal.X + metrics.Advance.X,
// penpal.Y + metrics.HorizontalBearing.Y,
// penpal.X + metrics.HorizontalBearing.X,
// penpal.Y - metrics.Size.Y + metrics.HorizontalBearing.Y),
// metrics.Location
// )
// );
// penpal.X += metrics.Advance.X;
}
}
else
{
for (int i = 0; i < block.Text.Length; i++)
{
char chr = block.Text[i];
// block.Font.GetCharacter(chr, out QuikTexture texture, out QGlyphMetrics metrics);
// group.Add(
// new TypesetCharacter(
// chr,
// texture,
// new QRectangle(
// penpal.X + metrics.Advance.X,
// penpal.Y + metrics.HorizontalBearing.Y,
// penpal.X + metrics.HorizontalBearing.X,
// penpal.Y - metrics.Size.Y + metrics.HorizontalBearing.Y),
// metrics.Location
// )
// );
// penpal.X += metrics.Advance.X;
}
}
penpal.X += interblockWs;
}
penpal.X -= interblockWs;
pen = penpal;
}
protected override void AppendBlock(object font, string text, bool rtl = false)
{
Blocks.Add(new HorizontalTextBlock(font, text, rtl));
}
}
public class VerticalParagraph : Paragraph
{
public override bool IsVertical => true;
public List<VerticalTextBlock> Blocks { get; } = new List<VerticalTextBlock>();
public override void Typeset(TypesetGroup group, float width)
{
throw new NotImplementedException();
}
protected override void AppendBlock(object font, string text, bool rtl = false)
{
Blocks.Add(new VerticalTextBlock(font, text));
}
}
public struct TypesetCharacter
{
public int Character;
public QImage Texture;
public QRectangle Position;
public QRectangle UV;
public TypesetCharacter(
int chr,
QImage texture,
in QRectangle position,
in QRectangle uv)
{
Character = chr;
Texture = texture;
Position = position;
UV = uv;
}
}
public class TypesetGroup : ICollection<TypesetCharacter>
{
private int _count = 0;
private TypesetCharacter[] _array = Array.Empty<TypesetCharacter>();
public QRectangle BoundingBox;
public int Count => _count;
public bool IsReadOnly => false;
public void Add(TypesetCharacter item)
{
if (_count == _array.Length)
{
Array.Resize(ref _array, _array.Length + 256);
}
_array[_count++] = item;
}
public void Clear()
{
_count = 0;
}
public void Translate(QVec2 offset)
{
BoundingBox.Translate(offset);
for (int i = 0; i < _count; i++)
{
_array[i].Position.Translate(offset);
}
}
public bool Contains(TypesetCharacter item)
{
throw new NotSupportedException();
}
public void CopyTo(TypesetCharacter[] array, int arrayIndex)
{
_array.CopyTo(array, arrayIndex);
}
public IEnumerator<TypesetCharacter> GetEnumerator()
{
for (int i = 0; i < _count; i++)
{
yield return _array[i];
}
}
public bool Remove(TypesetCharacter item)
{
throw new NotSupportedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator)GetEnumerator();
}
public void SortBy(IComparer<TypesetCharacter> comparer)
{
Array.Sort(_array, 0, _count, comparer);
}
public static IComparer<TypesetCharacter> SortByTexture { get; } = new SortByTextureComparer();
private class SortByTextureComparer : IComparer<TypesetCharacter>
{
public int Compare(TypesetCharacter x, TypesetCharacter y)
{
return y.Texture.GetHashCode() - x.Texture.GetHashCode();
}
}
}
/// <summary>
/// An enumeration of possible text alignments.
/// </summary>
[Flags]
public enum TextAlignment
{
/// <summary>
/// Align to the left margin, horizontally.
/// </summary>
AlignLeft = 0x01,
/// <summary>
/// Align text to center of left and right margins.
/// </summary>
AlignCenterH = 0x03,
/// <summary>
/// Align to the right margin, horizontally.
/// </summary>
AlignRight = 0x02,
/// <summary>
/// A bitmask for values relating to horizontal alignment.
/// </summary>
HorizontalMask = 0x03,
/// <summary>
/// Align text to the top margin.
/// </summary>
AlignTop = 0x00,
/// <summary>
/// Align text between the top and bottom margins.
/// <summary>
AlignCenterV = 0x04,
/// <summary>
/// Align text to the bottom margin.
/// </summary>
AlignBottom = 0x08,
/// <summary>
/// A bitmask for values relating to the vertical alignment.
/// </summary>
VerticalMask = 0x0C,
/// <summary>
/// Distribute characters uniformly on the line, when possible.
/// <summary>
Justify = 0x10,
/// <summary>
/// The default text alignment value.
/// </summary>
Default = AlignTop | AlignLeft
}
}
-181
View File
@@ -1,181 +0,0 @@
using Dashboard.CommandMachine;
using Dashboard.Media;
using System;
using System.Collections.Generic;
using System.Text;
namespace Dashboard.Typography
{
public static class Typesetter
{
private ref struct LineEnumerator
{
private ReadOnlySpan<char> Entire, Segment;
private bool Final;
public ReadOnlySpan<char> Current => Segment;
public LineEnumerator(ReadOnlySpan<char> value)
{
Entire = value;
Segment = ReadOnlySpan<char>.Empty;
Final = false;
}
public void Reset()
{
Segment = ReadOnlySpan<char>.Empty;
Final = false;
}
public bool MoveNext()
{
if (Final)
{
return false;
}
else if (Segment == ReadOnlySpan<char>.Empty)
{
int index = Entire.IndexOf('\n');
if (index == -1)
{
Segment = Entire;
}
else
{
Segment = Entire.Slice(0, index);
}
return true;
}
else
{
Entire.Overlaps(Segment, out int offset);
if (offset + Segment.Length >= Entire.Length)
{
return false;
}
ReadOnlySpan<char> rest = Entire.Slice(offset + Segment.Length + 1);
int index = rest.IndexOf('\n');
if (index == -1)
{
Segment = rest;
Final = true;
}
else
{
Segment = rest.Slice(0, index);
}
return true;
}
}
}
public static QVec2 MeasureHorizontal(ReadOnlySpan<char> str, float size, QFont font)
{
var enumerator = new LineEnumerator(str);
float width = 0.0f;
float height = 0.0f;
while (enumerator.MoveNext())
{
ReadOnlySpan<char> line = enumerator.Current;
float lineHeight = 0.0f;
foreach (Rune r in line.EnumerateRunes())
{
int codepoint = r.Value;
font.Get(codepoint, size, out FontGlyph glyph);
width += glyph.Metrics.Advance.X;
lineHeight = Math.Max(lineHeight, glyph.Metrics.Size.Y);
}
height += lineHeight;
}
return new QVec2(width, height);
}
public static void TypesetHorizontalDirect(this CommandList list, ReadOnlySpan<char> str, QVec2 origin, float size, QFont font)
{
Dictionary<QImage, FontDrawInfo> drawInfo = new Dictionary<QImage, FontDrawInfo>();
var enumerator = new LineEnumerator(str);
QVec2 pen = origin;
while (enumerator.MoveNext())
{
ReadOnlySpan<char> line = enumerator.Current;
float rise = 0.0f;
float fall = 0.0f;
// Find out all the code pages required, and the line height.
foreach (Rune r in line.EnumerateRunes())
{
int codepoint = r.Value;
font.Get(codepoint, size, out FontGlyph glyph);
float crise = glyph.Metrics.HorizontalBearing.Y;
float cfall = glyph.Metrics.Size.Y - crise;
rise = Math.Max(crise, rise);
fall = Math.Max(cfall, fall);
}
pen += new QVec2(0, rise);
foreach (Rune r in line.EnumerateRunes())
{
FontDrawInfo info;
int codepoint = r.Value;
font.Get(codepoint, size, out FontGlyph glyph);
ref readonly QGlyphMetrics metrics = ref glyph.Metrics;
QImage? image = glyph.Image;
if (image == null)
{
pen += new QVec2(metrics.Advance.X, 0);
continue;
}
if (!drawInfo.TryGetValue(image, out info))
{
info = new FontDrawInfo();
info.Image = image;
info.rectangles = new List<QRectangle>();
drawInfo[image] = info;
}
QRectangle dest = new QRectangle(
pen + new QVec2(metrics.HorizontalBearing.X + metrics.Size.X, metrics.Size.Y - metrics.HorizontalBearing.Y),
pen + new QVec2(metrics.HorizontalBearing.X, -metrics.HorizontalBearing.Y));
info.rectangles.Add(dest);
info.rectangles.Add(glyph.UVs);
pen.X += metrics.Advance.X;
}
pen.X = origin.X;
pen.Y += fall;
}
// Now for each rectangle we can dispatch draw calls.
foreach (FontDrawInfo info in drawInfo.Values)
{
list.Image(info.Image, info.rectangles.ToArray(), true);
}
}
private struct FontDrawInfo
{
public QImage Image;
public List<QRectangle> rectangles;
}
}
}
-108
View File
@@ -1,108 +0,0 @@
using System;
using System.Text;
namespace Dashboard.Typography
{
public static class UnicodeUtil
{
public static bool IsWhiteSpace(int chr)
{
switch (chr)
{
case '\t': case '\n': case '\v': case '\f': case '\r': case ' ':
case '\u0085': case '\u00A0': case '\u1680': case '\u2000':
case '\u2001': case '\u2002': case '\u2003': case '\u2004':
case '\u2005': case '\u2006': case '\u2007': case '\u2008':
case '\u2009': case '\u200A': case '\u2028': case '\u2029':
case '\u202F': case '\u205F': case '\u3000': case '\u180E':
case '\u200B': case '\u200C': case '\u200D': case '\u2060':
return true;
default:
return false;
}
}
public static bool IsUnicodeWhitespace(int chr)
{
switch (chr)
{
case '\t': case '\n': case '\v': case '\f': case '\r': case ' ':
case '\u0085': case '\u00A0': case '\u1680': case '\u2000':
case '\u2001': case '\u2002': case '\u2003': case '\u2004':
case '\u2005': case '\u2006': case '\u2007': case '\u2008':
case '\u2009': case '\u200A': case '\u2028': case '\u2029':
case '\u202F': case '\u205F': case '\u3000':
return true;
default:
return false;
}
}
public static bool IsNewLine(int chr)
{
switch (chr)
{
case '\n': case '\v': case '\f': case '\r':
case '\u0085': case '\u2028': case '\u2029':
return true;
default:
return false;
}
}
public static WhitespaceInfo GetWhitespaceInfo(int chr)
{
switch (chr)
{
// CHAR ------------BOILERPLATE----------- BREAK NEWLINE
case '\t': return new WhitespaceInfo('\t', true, false);
case '\n': return new WhitespaceInfo('\n', false, true);
case '\v': return new WhitespaceInfo('\v', false, true);
case '\f': return new WhitespaceInfo('\f', false, true);
case '\r': return new WhitespaceInfo('\r', false, true);
case ' ': return new WhitespaceInfo(' ', true, false);
case '\u0085': return new WhitespaceInfo('\u0085', false, true);
case '\u00A0': return new WhitespaceInfo('\u00A0', false, false);
case '\u1680': return new WhitespaceInfo('\u1689', true, false);
case '\u2000': return new WhitespaceInfo('\u2000', true, false);
case '\u2001': return new WhitespaceInfo('\u2001', true, false);
case '\u2002': return new WhitespaceInfo('\u2002', true, false);
case '\u2003': return new WhitespaceInfo('\u2003', true, false);
case '\u2004': return new WhitespaceInfo('\u2004', true, false);
case '\u2005': return new WhitespaceInfo('\u2005', true, false);
case '\u2006': return new WhitespaceInfo('\u2006', true, false);
case '\u2007': return new WhitespaceInfo('\u2007', false, false);
case '\u2008': return new WhitespaceInfo('\u2008', true, false);
case '\u2009': return new WhitespaceInfo('\u2009', true, false);
case '\u200A': return new WhitespaceInfo('\u200A', true, false);
case '\u2028': return new WhitespaceInfo('\u2028', false, true);
case '\u2029': return new WhitespaceInfo('\u2029', false, true);
case '\u202F': return new WhitespaceInfo('\u202F', false, false);
case '\u205F': return new WhitespaceInfo('\u205F', true, false);
case '\u3000': return new WhitespaceInfo('\u3000', true, false);
case '\u180E': return new WhitespaceInfo('\u180E', true, false);
case '\u200B': return new WhitespaceInfo('\u200B', true, false);
case '\u200C': return new WhitespaceInfo('\u200C', true, false);
case '\u200D': return new WhitespaceInfo('\u200D', true, false);
case '\u2060': return new WhitespaceInfo('\u2060', false, false);
default:
throw new ArgumentException("Character is not a whitespace character.", nameof(chr));
}
}
}
public struct WhitespaceInfo
{
public int Character { get; }
public bool IsBreaking { get; }
public bool IsNewline { get; }
public WhitespaceInfo(int chr, bool breaking, bool nl)
{
Character = chr;
IsBreaking = breaking;
IsNewline = nl;
}
}
}
-113
View File
@@ -1,113 +0,0 @@
using Dashboard.Media;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace Dashboard.VertexGenerator
{
public class DrawQueue : IEnumerable<DrawCall>
{
private readonly RefList<QuikVertex> _vertices = new RefList<QuikVertex>();
private readonly RefList<int> _elements = new RefList<int>();
private readonly List<DrawCall> _drawCalls = new List<DrawCall>();
private int _start;
private int _baseOffset;
private QRectangle _bounds;
private QImage? _texture;
public int ZDepth { get; private set; }
public QuikVertex[] VertexArray => _vertices.InternalArray;
public int VertexCount => _vertices.Count;
public int[] ElementArray => _elements.InternalArray;
public int ElementCount => _elements.Count;
public int DrawCallCount => _drawCalls.Count;
public int BaseOffset => _baseOffset;
public void Clear()
{
_vertices.Clear();
_elements.Clear();
_drawCalls.Clear();
ZDepth = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void StartDrawCall(in QRectangle bounds, QImage? texture, int baseOffset)
{
_start = ElementCount;
_texture = texture;
_bounds = bounds;
_baseOffset = baseOffset;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void StartDrawCall(in QRectangle bounds) => StartDrawCall(bounds, null, _vertices.Count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void StartDrawCall(in QRectangle bounds, int baseOffset) => StartDrawCall(bounds, null, baseOffset);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void StartDrawCall(in QRectangle bounds, QImage texture) => StartDrawCall(bounds, texture, _vertices.Count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddVertex(in QuikVertex vertex)
{
_vertices.Add(in vertex);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddElement(int offset)
{
_elements.Add(offset + _baseOffset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int RestoreOffset(int baseOffset)
{
int old = _baseOffset;
_baseOffset = baseOffset;
return old;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int RestoreOffset() => RestoreOffset(_vertices.Count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int AbsoluteElement(int offset)
{
return _baseOffset + offset;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int RelativeElement(int baseOffset, int offset)
{
return AbsoluteElement(offset) - baseOffset;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EndDrawCall()
{
int count = ElementCount - _start;
_drawCalls.Add(new DrawCall(_start, count, _bounds, _texture));
}
public IEnumerator<DrawCall> GetEnumerator() => _drawCalls.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _drawCalls.GetEnumerator();
}
public struct DrawCall
{
public int Start { get; }
public int Count { get; }
public QRectangle Bounds { get; }
public QImage? Texture { get; }
public DrawCall(int start, int count, in QRectangle bounds, QImage? texture)
{
Start = start;
Count = count;
Bounds = bounds;
Texture = texture;
}
}
}
-43
View File
@@ -1,43 +0,0 @@
using System.Diagnostics;
namespace Dashboard.VertexGenerator
{
/// <summary>
/// Represents a GPU vertex.
/// </summary>
[DebuggerDisplay("XY={Position} RGBA={Color}, UV={TextureCoordinates}")]
public struct QuikVertex
{
/// <summary>
/// Position value.
/// </summary>
public QVec2 Position;
/// <summary>
/// Texture Coordinates.
/// </summary>
public QVec2 TextureCoordinates;
/// <summary>
/// Per vertex color value.
/// </summary>
public QColor Color;
/// <summary>
/// Per vertex depth index value.
/// </summary>
public int ZIndex;
/// <summary>
/// The texture layer to draw for 3d images.
/// </summary>
public float TextureLayer;
public static int PositionOffset => 0;
public static unsafe int TextureCoordinatesOffset => sizeof(QVec2);
public static unsafe int ColorOffset => 2 * sizeof(QVec2);
public static unsafe int ZIndexOffset => ColorOffset + sizeof(QColor);
public static unsafe int TextureLayerOffset => ZIndexOffset + sizeof(int);
public static unsafe int Stride => sizeof(QuikVertex);
}
}
-48
View File
@@ -1,48 +0,0 @@
using System;
namespace Dashboard.VertexGenerator
{
/// <summary>
/// A small list which whose items can be used by reference.
/// </summary>
/// <typeparam name="T">Container type.</typeparam>
public class RefList<T>
{
private T[] _array = Array.Empty<T>();
private int _count = 0;
public T[] InternalArray => _array;
public ref T this[int index] => ref _array[index];
public int Count => _count;
public int Capacity => _array.Length;
public void Add(in T item)
{
EnsureCapacity(Count + 1);
this[_count++] = item;
}
public void Add(T item)
{
EnsureCapacity(Count + 1);
this[_count++] = item;
}
public void Clear()
{
Array.Resize(ref _array, 0);
_count = 0;
}
private void EnsureCapacity(int needed)
{
while (_array.Length < needed)
{
Array.Resize(ref _array, Math.Max(1, _array.Length) * 2);
}
}
}
}
File diff suppressed because it is too large Load Diff
-61
View File
@@ -1,61 +0,0 @@
/**
* QUIK: User Interface Kit
* Copyright (C) 2023 Halit Utku Maden, et al.
*/
#version 130
in vec2 fv2TexPos;
in vec4 fv4Color;
in float ffTexLayer;
out vec4 fragColor;
uniform int iEnableSdf;
uniform int iEnableTexture;
uniform int iAlphaDiscard;
uniform float fSdfThreshold = 0.5;
uniform sampler2D tx2d;
const float fAlphaThreshold = 0.01;
vec4 getTexture()
{
if (iEnableTexture == 3)
{
// return texture(tx2dArray, vec3(fv2TexPos, ffTexLayer));
}
else if (iEnableSdf == 1)
{
vec2 texelSz = 1.0/vec2(textureSize(tx2d, 0));
vec2 txCoord2 = fv2TexPos + texelSz * (1 - mod(fv2TexPos, texelSz));
return texture(tx2d, txCoord2);
}
else
{
return texture(tx2d, fv2TexPos);
}
}
void main(void)
{
vec4 albedo = fv4Color;
if (iEnableTexture != 0)
{
vec4 value = getTexture();
if (iEnableSdf != 0)
{
value = vec4(vec3(1.0), smoothstep(fSdfThreshold-0.1, fSdfThreshold+0.1, value.r));
}
if (iAlphaDiscard != 0 && value.a <= fAlphaThreshold)
{
discard;
}
albedo = albedo * value;
}
fragColor = albedo;
}
-35
View File
@@ -1,35 +0,0 @@
/**
* QUIK: User Interface Kit
* Copyright (C) 2023 Halit Utku Maden, et al.
*/
#version 130
in vec2 v2Position; /**< The vertex position.*/
in float fZIndex; /**< The z index. */
in vec2 v2TexPos; /**< The texture coorindates. */
in float fTexLayer; /**< The texture layer for 3D textures. */
in vec4 v4Color; /**< The vertex color. */
out vec2 fv2TexPos;
out float ffTexLayer;
out vec4 fv4Color;
uniform mat4 m4Transforms; /**< The view matrix. */
uniform float fMaxZ; /**< Highest Z coordinate. */
const mat4 m4BaseTransforms = mat4(
vec4( 2.0, 0.0, 0.0, 0.0),
vec4( 0.0, -2.0, 0.0, 0.0),
vec4( 0.0, 0.0, 1.0, 0.0),
vec4(-1.0, 1.0, 0.0, 1.0)
);
void main(void)
{
vec4 v = vec4(v2Position, fZIndex/fMaxZ, 1);
gl_Position = m4Transforms * v;
fv2TexPos = v2TexPos;
fv4Color = v4Color;
ffTexLayer = fTexLayer;
}
+74 -20
View File
@@ -1,23 +1,77 @@
![QUIK](https://git.mixedup.dev/QUIK/Quik.Common/raw/branch/master/assets/quik.svg) QUIK: User Interface Kit
====================================================================================================================
QUIK is an immediate mode user interface kit intended for embedding in other
C# applications. When rendering UI elements, a command buffer is generated may
can be converted to any other rendering commands using built-in or self made
procedures.
Dashboard
=========
Dashboard is a modular immediate rendering and UI toolkit that is intended to
develop modular graphics applications in .NET. The library comes in layers,
with no tight coupling between each stage. It makes Dashboard an excellent
library to embed into your OpenGL, Vulkan or DirectX applications as a UI or
HUD rendering toolkit.
Why QUIK?
---------
QUIK was inspired by Dear ImGUI and Nuklear projects, however these libraries
being implemented in C++ and C respectively make it harder to use them in a
C#.NET environment, either requiring excessive P/Invoke or custom made, error
prone marshalling.
### Scope
Generally speaking, this library is not going to replace a professional UI
toolkit for most .NET programmers. The 1st and 3rd party frameworks available
are already excellent for most people. As such, the library does not follow
any UI development discipline, like MVVM. This is supposed to be easy to
integrate, customize and deliver. You are free to pick and choose which
components you like, hate, include and exclude.
QUIK is not intended to replace the aforementioned libraries for the C or C++
developer, however it is intended to make a similar library available to C#
users without having to battle the un/managed barrier. It also comes with the
advantage of not requiring any platform specific native libraries.
### Availability
This library is currently under development and is not intended for end users
until further notice. Things are subject to change without notice, or
deprecation. You have been warned. And we do not talk about the original
QUIK/Dashboard code base. It's a mess.
Dashboard.Drawing - The Immediate Rendering System
----------------------------------------------------
The Dashboard immediate rendering system provides a framework for issuing
rendering commands independtly of the graphics backend present. In order
to draw anything, you must issue enqueue drawing commands into a queue.
This queue is later consumed by the graphics backend in order to reach a
final image.
The main benefit of the graphics queue is that you can write your graphics calls
once and expect the same results, at least approximately. As an advanced
example, you could write a DrawQueue to PostScript transpiler, which can be
consumed by a tool like ghostscript to conver it into a PDF file. And with the
same DrawQueue contents, you can also render the same thing with OpenGL, or your
custom software renderer.
Each draw queue is going to contain the following main components:
- The image bounding box.
- The list of extensions required to draw the image.
- The list of resources referenced by the draw queue.
- The list of commands that have been used in the draw queue.
- The stream of bytes that make up the draw queue.
This creates an extremely de/serializable command queue that can be generated
and interpreted with ease.
### Coordinate Systems and Measures.
The coordinate system is assumed to be left handed and with a top-left origin.
The length unit is device independent points (pt). Without accounting for high
pixel density displays, 1pt is 96/72 pixels (~1.3). The angle unit is degrees.
These set of conventions set herein follow the conventions set by traditional
computer aided graphics design. Following these conventions is entirely
optional, if and when you see fit.
### Extensions
A drawing extension defines a set of features and commands that need to be
understood to generate the image correctly. Extensions are simple strings that
are valid ISO C99 identifiers. This means that they must only contain
alphanumeric characters, or an underscore, and must not begin with a number.
The prefix `DB_` is reserved for the Dashboard library itself. Please refrain
from making an extension with this prefix. We recommend naming your extensions
with an underscore case name. Also please consider including a vendor prefix,
such as our own `DB_`. If you do not wish to do so, please consider your vendor
prefix as `X_`.
## Special Thanks
I would like to thank the following people, in no particular order, for their
contributions in this endevour:
- Callum McGing for BlurgText, the text rendering library.
- "BoyBayKiller", for providing valuable feedback for the libraries that spun off of Dashboard.
- "Froil" for coining the name Dashboard, fitting with the suite of ReFuel libraries, after the inevitable issues that the name QUIK has caused.
On top of that, QUIK targets not just .NET 6.0, but old-school .NET framework
in mind as well. Whilst I do not promise the golden .NET Framework 2.0 support
I do wish to make the library available for .NET Framework 4.7.2 in the future.
Therefore the language version of the project is strictly limited to 7.3.
-7
View File
@@ -1,7 +0,0 @@
services:
cmdline:
image: quik/quik
user: quik
working_dir: /home/quik
volumes:
- .:/home/quik/src
@@ -1,16 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\..\Dashboard\Dashboard.csproj" />
<ProjectReference Include="..\..\Dashboard.Media.Defaults\Dashboard.Media.Defaults.csproj" />
<ProjectReference Include="..\..\Dashboard.OpenTK\Dashboard.OpenTK.csproj" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
-44
View File
@@ -1,44 +0,0 @@
using Dashboard;
using Dashboard.CommandMachine;
using Dashboard.Controls;
using Dashboard.OpenTK;
using Dashboard.Media.Defaults;
using Dashboard.Media;
using Dashboard.PAL;
namespace Dashboard.Demo
{
public static class Program
{
public static readonly DashboardApplication Application = new DashboardApplication(new OpenTKPlatform());
public static void Main(string[] args)
{
Application.Run(new EmptyView());
}
}
public class EmptyView : View
{
private QFont? font;
private readonly Label Label = new Label() { Text = "Hello world!", Position = new QVec2(300, 300) };
protected override void PaintBegin(CommandList cmd)
{
base.PaintBegin(cmd);
if (font == null)
{
IFontDataBase db = FontDataBaseProvider.Instance;
font = new QFontFreeType(db.FontFileInfo(db.Sans).OpenRead());
Label.Font = font;
Label.TextSize = 12;
Label.InvalidateVisual();
}
cmd.Rectangle(new QRectangle(16, 16, 0, 0));
Label.Paint(cmd);
}
}
}
@@ -1,13 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BlurgText" Version="0.1.0-nightly-4" />
<ProjectReference Include="..\..\Dashboard.Drawing\Dashboard.Drawing.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,10 @@
using Dashboard.Drawing;
using System.Diagnostics;
using System.Drawing;
using System.Numerics;
DrawQueue queue = new DrawQueue();
queue.Point(Vector3.Zero, 2, new SolidBrush(Color.White));
Debugger.Break();