Compare commits
42 Commits
a1573d3786
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| dadd9f137a | |||
| 23fc485b2a | |||
| c6c76aa8be | |||
| 6670667ff6 | |||
| d06264e424 | |||
| a1f4e6a4dc | |||
| 470475092a | |||
| 1ee492ccd4 | |||
| 3b52649ad2 | |||
| f8a4c73367 | |||
| 88e060a92b | |||
| 1ccab1c85a | |||
| 55e9d775cd | |||
| 8fd00257d3 | |||
| 82d2027cf3 | |||
| 9b16e015f6 | |||
| ff4a158cdb | |||
| 29829fd299 | |||
| 19cc765955 | |||
| ce2a569a20 | |||
| ab1849a891 | |||
| 3ae107c83e | |||
| bd69c0d93f | |||
| 279e619c3b | |||
| 7cb47c721b | |||
| 9105b16df8 | |||
| 3b73090f79 | |||
| 3484dce8c5 | |||
| 3418537b43 | |||
| d831c9b72d | |||
| 66644be699 | |||
| 39dfca18f2 | |||
| 75a11adbe7 | |||
| 21233c8173 | |||
| ccb0c6ffe7 | |||
| bc3dcff3ea | |||
| 2aa1066a9d | |||
| fe3c2d0550 | |||
| 5aa9a2f4e6 | |||
| 4d5e0dd8f2 | |||
| f07208ebe9 | |||
| b57d7bed41 |
8
.gitmodules
vendored
8
.gitmodules
vendored
@@ -1,8 +0,0 @@
|
|||||||
[submodule "lib/stb"]
|
|
||||||
path = lib/stb
|
|
||||||
url = https://github.com/nothings/stb.git
|
|
||||||
[submodule "lib/freetype"]
|
|
||||||
path = lib/freetype
|
|
||||||
url = https://gitlab.freedesktop.org/freetype/freetype.git
|
|
||||||
[submodule "freetype"]
|
|
||||||
branch = VER-2-9-1
|
|
||||||
13
Dashboard.BlurgText/Dashboard.BlurgText.csproj
Normal file
13
Dashboard.BlurgText/Dashboard.BlurgText.csproj
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="BlurgText" Version="0.1.0-nightly-4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
19
Dashboard.Media.Defaults/Dashboard.Media.Defaults.csproj
Normal file
19
Dashboard.Media.Defaults/Dashboard.Media.Defaults.csproj
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<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>
|
||||||
13
Dashboard.Media.Defaults/EnvironmentVariables.cs
Normal file
13
Dashboard.Media.Defaults/EnvironmentVariables.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
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";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using Quik.FreeType;
|
using ReFuel.FreeType;
|
||||||
|
|
||||||
namespace Quik.Media.Defaults
|
namespace Dashboard.Media.Defaults
|
||||||
{
|
{
|
||||||
public static class FTProvider
|
public static class FTProvider
|
||||||
{
|
{
|
||||||
269
Dashboard.Media.Defaults/Fallback/FallbackFontDatabase.cs
Normal file
269
Dashboard.Media.Defaults/Fallback/FallbackFontDatabase.cs
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
93
Dashboard.Media.Defaults/FontDatabaseProvider.cs
Normal file
93
Dashboard.Media.Defaults/FontDatabaseProvider.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Dashboard.Media.Defaults/FreeTypeFontFactory.cs
Normal file
24
Dashboard.Media.Defaults/FreeTypeFontFactory.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
230
Dashboard.Media.Defaults/Linux/FontConfig.cs
Normal file
230
Dashboard.Media.Defaults/Linux/FontConfig.cs
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
168
Dashboard.Media.Defaults/Linux/FontConfigFontDatabase.cs
Normal file
168
Dashboard.Media.Defaults/Linux/FontConfigFontDatabase.cs
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Dashboard.Media.Defaults/Linux/LinuxFonts.cs
Normal file
11
Dashboard.Media.Defaults/Linux/LinuxFonts.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
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
Dashboard.Media.Defaults/QFontFreeType.cs
Normal file
83
Dashboard.Media.Defaults/QFontFreeType.cs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,22 +1,22 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using Dashboard.Media.Color;
|
||||||
using Quik.Media;
|
using ReFuel.Stb;
|
||||||
using Quik.Media.Color;
|
|
||||||
using Quik.Stb;
|
|
||||||
|
|
||||||
namespace Quik.Media.Defaults
|
namespace Dashboard.Media.Defaults
|
||||||
{
|
{
|
||||||
public unsafe class QImageStbi : QImage
|
public unsafe class QImageStbi : QImage
|
||||||
{
|
{
|
||||||
private readonly StbImage image;
|
private readonly StbImage image;
|
||||||
private QImageBuffer buffer;
|
private QImageBuffer buffer;
|
||||||
|
private bool isSdf = false;
|
||||||
|
|
||||||
public override int Width => image.Width;
|
public override int Width => image.Width;
|
||||||
|
|
||||||
public override int Height => image.Height;
|
public override int Height => image.Height;
|
||||||
|
|
||||||
public override int Depth => 1;
|
public override int Depth => 1;
|
||||||
|
public override bool IsSdf => isSdf;
|
||||||
public override QImageFormat InternalFormat => Stb2QImageFormat(image.Format);
|
public override QImageFormat InternalFormat => Stb2QImageFormat(image.Format);
|
||||||
|
|
||||||
public QImageStbi(Stream source)
|
public QImageStbi(Stream source)
|
||||||
@@ -79,6 +79,11 @@ namespace Quik.Media.Defaults
|
|||||||
buffer.UnlockBits();
|
buffer.UnlockBits();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SdfHint(bool value = true)
|
||||||
|
{
|
||||||
|
isSdf = value;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
@@ -4,14 +4,14 @@ using System.Collections;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Quik.Media;
|
using Dashboard.Media.Font;
|
||||||
|
|
||||||
// WebRequest is obsolete but runs on .NET framework.
|
// WebRequest is obsolete but runs on .NET framework.
|
||||||
#pragma warning disable SYSLIB0014
|
#pragma warning disable SYSLIB0014
|
||||||
|
|
||||||
namespace Quik.Media.Defaults
|
namespace Dashboard.Media.Defaults
|
||||||
{
|
{
|
||||||
public class StbMediaLoader : MediaLoader<string>, MediaLoader<Uri>, MediaLoader<FileInfo>, MediaLoader<FontInfo>
|
public class StbMediaLoader : MediaLoader<string>, MediaLoader<Uri>, MediaLoader<FileInfo>, MediaLoader<FontFace>
|
||||||
{
|
{
|
||||||
public bool AllowRemoteTransfers { get; set; } = false;
|
public bool AllowRemoteTransfers { get; set; } = false;
|
||||||
private readonly ArrayPool<byte> ByteArrays = ArrayPool<byte>.Create();
|
private readonly ArrayPool<byte> ByteArrays = ArrayPool<byte>.Create();
|
||||||
@@ -31,9 +31,9 @@ namespace Quik.Media.Defaults
|
|||||||
{
|
{
|
||||||
return GetMedia((FileInfo)key, hint);
|
return GetMedia((FileInfo)key, hint);
|
||||||
}
|
}
|
||||||
else if (t == typeof(FontInfo))
|
else if (t == typeof(FontFace))
|
||||||
{
|
{
|
||||||
return GetMedia((FontInfo)key, hint);
|
return GetMedia((FontFace)key, hint);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -56,7 +56,7 @@ namespace Quik.Media.Defaults
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDisposable GetMedia(FontInfo key, MediaHint hint)
|
public IDisposable GetMedia(FontFace key, MediaHint hint)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
151
Dashboard.Media.Defaults/Win32/EnumerateFonts.cs
Normal file
151
Dashboard.Media.Defaults/Win32/EnumerateFonts.cs
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
#if false
|
||||||
|
namespace Quik.Media.Defaults.Win32
|
||||||
|
{
|
||||||
|
|
||||||
|
public class EnumerateFonts
|
||||||
|
{
|
||||||
|
private const byte DEFAULT_CHARSET = 1;
|
||||||
|
|
||||||
|
public static void Enumerate(FontFace font)
|
||||||
|
{
|
||||||
|
/* It's windows, just borrow the desktop window. */
|
||||||
|
IntPtr hdc = GetDC(GetDesktopWindow());
|
||||||
|
|
||||||
|
List<(LogFontA, TextMetricA)> list = new List<(LogFontA, TextMetricA)>();
|
||||||
|
|
||||||
|
LogFontA font2 = new LogFontA()
|
||||||
|
{
|
||||||
|
//FaceName = font.Family,
|
||||||
|
Weight = ((font.Style & FontStyle.Bold) != 0) ? FontWeight.Bold : FontWeight.Regular,
|
||||||
|
Italic = (font.Style & FontStyle.Italic) != 0,
|
||||||
|
CharSet = DEFAULT_CHARSET
|
||||||
|
};
|
||||||
|
|
||||||
|
Console.WriteLine(font2.FaceName);
|
||||||
|
|
||||||
|
EnumFontFamiliesExProc proc = (in LogFontA font, in TextMetricA metric, int type, IntPtr lparam) =>
|
||||||
|
{
|
||||||
|
list.Add((font, metric));
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
EnumFontFamiliesExA(hdc, font2, proc, IntPtr.Zero, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private const string gdi32 = "Gdi32.dll";
|
||||||
|
private const string user32 = "User32.dll";
|
||||||
|
|
||||||
|
[DllImport(gdi32)]
|
||||||
|
private static extern int EnumFontFamiliesExA(
|
||||||
|
IntPtr hdc,
|
||||||
|
in LogFontA font,
|
||||||
|
[MarshalAs(UnmanagedType.FunctionPtr)] EnumFontFamiliesExProc proc,
|
||||||
|
IntPtr lparam,
|
||||||
|
int flags /* Should be zero. */);
|
||||||
|
|
||||||
|
[DllImport(user32)]
|
||||||
|
private static extern IntPtr /* HWND */ GetDesktopWindow();
|
||||||
|
|
||||||
|
[DllImport(user32)]
|
||||||
|
private static extern IntPtr /* HDC */ GetDC(IntPtr hwnd);
|
||||||
|
|
||||||
|
private delegate int EnumFontFamiliesExProc(in LogFontA font, in TextMetricA metric, int fontType, IntPtr lParam);
|
||||||
|
|
||||||
|
private struct LogFontA
|
||||||
|
{
|
||||||
|
public long Height;
|
||||||
|
public long Width;
|
||||||
|
public long Escapement;
|
||||||
|
public long Orientation;
|
||||||
|
public FontWeight Weight;
|
||||||
|
[MarshalAs(UnmanagedType.U1)]
|
||||||
|
public bool Italic;
|
||||||
|
[MarshalAs(UnmanagedType.U1)]
|
||||||
|
public bool Underline;
|
||||||
|
[MarshalAs(UnmanagedType.U1)]
|
||||||
|
public bool StrikeOut;
|
||||||
|
public byte CharSet;
|
||||||
|
public byte OutPrecision;
|
||||||
|
public byte ClipPrecision;
|
||||||
|
public byte PitchAndFamily;
|
||||||
|
private unsafe fixed byte aFaceName[32];
|
||||||
|
public unsafe string FaceName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
fixed (byte* str = aFaceName)
|
||||||
|
{
|
||||||
|
int len = 0;
|
||||||
|
for (; str[len] != 0 && len < 32; len++) ;
|
||||||
|
return Encoding.UTF8.GetString(str, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
fixed (byte *str = aFaceName)
|
||||||
|
{
|
||||||
|
Span<byte> span = new Span<byte>(str, 32);
|
||||||
|
Encoding.UTF8.GetBytes(value, span);
|
||||||
|
span[31] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct TextMetricA
|
||||||
|
{
|
||||||
|
public long Height;
|
||||||
|
public long Ascent;
|
||||||
|
public long Descent;
|
||||||
|
public long InternalLeading;
|
||||||
|
public long ExternalLeading;
|
||||||
|
public long AveCharWidth;
|
||||||
|
public long MaxCharWidth;
|
||||||
|
public long Weight;
|
||||||
|
public long Overhang;
|
||||||
|
public long DigitizedAspectX;
|
||||||
|
public long DigitizedAspectY;
|
||||||
|
public byte FirstChar;
|
||||||
|
public byte LastChar;
|
||||||
|
public byte DefaultChar;
|
||||||
|
public byte BreakChar;
|
||||||
|
public byte Italic;
|
||||||
|
public byte Underlined;
|
||||||
|
public byte StruckOut;
|
||||||
|
public byte PitchAndFamily;
|
||||||
|
public byte CharSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum FontWeight : long
|
||||||
|
{
|
||||||
|
DontCare = 0,
|
||||||
|
Thin = 100,
|
||||||
|
ExtraLight = 200,
|
||||||
|
UltraLight = 200,
|
||||||
|
Light = 300,
|
||||||
|
Normal = 400,
|
||||||
|
Regular = 400,
|
||||||
|
Medium = 500,
|
||||||
|
Semibold = 600,
|
||||||
|
Demibold = 600,
|
||||||
|
Bold = 700,
|
||||||
|
Extrabold = 800,
|
||||||
|
Ultrabold = 800,
|
||||||
|
Heavy = 900,
|
||||||
|
Black = 900
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Nullable>disable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>7.3</LangVersion>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -11,7 +10,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Quik\Quik.csproj" />
|
<ProjectReference Include="..\Dashboard\Dashboard.csproj" />
|
||||||
<EmbeddedResource Include="glsl\**"/>
|
<EmbeddedResource Include="glsl\**"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
104
Dashboard.OpenTK/OpenTKPlatform.cs
Normal file
104
Dashboard.OpenTK/OpenTKPlatform.cs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using OpenTK.Mathematics;
|
using OpenTK.Mathematics;
|
||||||
using OpenTK.Windowing.Desktop;
|
using OpenTK.Windowing.Desktop;
|
||||||
using Quik.OpenGL;
|
using Dashboard.OpenGL;
|
||||||
using Quik.CommandMachine;
|
using Dashboard.CommandMachine;
|
||||||
using Quik.PAL;
|
using Dashboard.PAL;
|
||||||
using Quik.VertexGenerator;
|
using Dashboard.VertexGenerator;
|
||||||
|
|
||||||
namespace Quik.OpenTK
|
namespace Dashboard.OpenTK
|
||||||
{
|
{
|
||||||
public class OpenTKPort : IQuikPortHandle
|
public class OpenTKPort : IDashHandle
|
||||||
{
|
{
|
||||||
private readonly NativeWindow _window;
|
private readonly NativeWindow _window;
|
||||||
private readonly GL21Driver _glDriver;
|
private readonly GL21Driver _glDriver;
|
||||||
@@ -51,7 +51,7 @@ namespace Quik.OpenTK
|
|||||||
|
|
||||||
public bool IsValid => !isDisposed;
|
public bool IsValid => !isDisposed;
|
||||||
|
|
||||||
public event EventHandler EventRaised;
|
public event EventHandler? EventRaised;
|
||||||
|
|
||||||
public OpenTKPort(NativeWindow window)
|
public OpenTKPort(NativeWindow window)
|
||||||
{
|
{
|
||||||
@@ -78,6 +78,7 @@ namespace Quik.OpenTK
|
|||||||
if (!_glDriver.IsInit)
|
if (!_glDriver.IsInit)
|
||||||
_glDriver.Init();
|
_glDriver.Init();
|
||||||
|
|
||||||
|
GL.Clear(GLEnum.GL_COLOR_BUFFER_BIT | GLEnum.GL_DEPTH_BUFFER_BIT);
|
||||||
_glDriver.Draw(_vertexEngine.DrawQueue, view);
|
_glDriver.Draw(_vertexEngine.DrawQueue, view);
|
||||||
|
|
||||||
_window.Context.SwapBuffers();
|
_window.Context.SwapBuffers();
|
||||||
51
Dashboard.sln
Normal file
51
Dashboard.sln
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
|
||||||
|
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}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Media.Defaults", "Dashboard.Media.Defaults\Dashboard.Media.Defaults.csproj", "{3798F6DD-8F84-4B7D-A810-B0D4B5ACB672}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.OpenTK", "Dashboard.OpenTK\Dashboard.OpenTK.csproj", "{2013470A-915C-46F2-BDD3-FCAA39C845EE}"
|
||||||
|
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}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = 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}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Quik.CommandMachine
|
namespace Dashboard.CommandMachine
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enumeration of built-in Quik commands.
|
/// Enumeration of built-in Quik commands.
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Quik.CommandMachine
|
namespace Dashboard.CommandMachine
|
||||||
{
|
{
|
||||||
public class CommandEngine
|
public class CommandEngine
|
||||||
{
|
{
|
||||||
@@ -20,7 +20,7 @@ namespace Quik.CommandMachine
|
|||||||
|
|
||||||
public QMat4 ActiveTransforms { get; }
|
public QMat4 ActiveTransforms { get; }
|
||||||
|
|
||||||
public StyleStack Style { get; } = new StyleStack(new Quik.Style());
|
public StyleStack Style { get; } = new StyleStack(new Style());
|
||||||
|
|
||||||
protected CommandEngine()
|
protected CommandEngine()
|
||||||
{
|
{
|
||||||
@@ -99,6 +99,16 @@ namespace Quik.CommandMachine
|
|||||||
case Command.PopZ:
|
case Command.PopZ:
|
||||||
_zIndex = _zStack.TryPop(out int zindex) ? zindex : 0;
|
_zIndex = _zStack.TryPop(out int zindex) ? zindex : 0;
|
||||||
break;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,5 +170,63 @@ namespace Quik.CommandMachine
|
|||||||
iterator.Dequeue();
|
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,4 +1,4 @@
|
|||||||
namespace Quik.CommandMachine
|
namespace Dashboard.CommandMachine
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A delegate for a QUIK command.
|
/// A delegate for a QUIK command.
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
|
using Dashboard.Media;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace Quik.CommandMachine
|
namespace Dashboard.CommandMachine
|
||||||
{
|
{
|
||||||
public class CommandList : IEnumerable<Frame>
|
public class CommandList : IEnumerable<Frame>
|
||||||
{
|
{
|
||||||
@@ -108,6 +108,23 @@ namespace Quik.CommandMachine
|
|||||||
Enqueue(Command.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)
|
public void Line(in QLine line)
|
||||||
{
|
{
|
||||||
Enqueue(Command.Line);
|
Enqueue(Command.Line);
|
||||||
@@ -214,7 +231,7 @@ namespace Quik.CommandMachine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Image(QuikTexture texture, in QRectangle rectangle)
|
public void Image(QImage texture, in QRectangle rectangle)
|
||||||
{
|
{
|
||||||
Enqueue(Command.Image);
|
Enqueue(Command.Image);
|
||||||
Enqueue((Frame)(int)ImageCommandFlags.Single);
|
Enqueue((Frame)(int)ImageCommandFlags.Single);
|
||||||
@@ -222,7 +239,7 @@ namespace Quik.CommandMachine
|
|||||||
Enqueue(rectangle);
|
Enqueue(rectangle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Image(QuikTexture texture, in QRectangle rectangle, in QRectangle uv)
|
public void Image(QImage texture, in QRectangle rectangle, in QRectangle uv)
|
||||||
{
|
{
|
||||||
Enqueue(Command.Image);
|
Enqueue(Command.Image);
|
||||||
Enqueue((Frame)(int)(ImageCommandFlags.Single | ImageCommandFlags.UVs));
|
Enqueue((Frame)(int)(ImageCommandFlags.Single | ImageCommandFlags.UVs));
|
||||||
@@ -231,7 +248,7 @@ namespace Quik.CommandMachine
|
|||||||
Enqueue(uv);
|
Enqueue(uv);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Image(QuikTexture texture, ReadOnlySpan<QRectangle> rectangles, bool interleavedUV = false)
|
public void Image(QImage texture, ReadOnlySpan<QRectangle> rectangles, bool interleavedUV = false)
|
||||||
{
|
{
|
||||||
int count = rectangles.Length;
|
int count = rectangles.Length;
|
||||||
ImageCommandFlags flags = ImageCommandFlags.None;
|
ImageCommandFlags flags = ImageCommandFlags.None;
|
||||||
@@ -243,7 +260,7 @@ namespace Quik.CommandMachine
|
|||||||
}
|
}
|
||||||
|
|
||||||
Enqueue(Command.Image);
|
Enqueue(Command.Image);
|
||||||
Enqueue(new Frame(count));
|
Enqueue(new Frame((int)flags, count));
|
||||||
Enqueue(new Frame(texture));
|
Enqueue(new Frame(texture));
|
||||||
|
|
||||||
foreach (QRectangle rectangle in rectangles)
|
foreach (QRectangle rectangle in rectangles)
|
||||||
@@ -252,7 +269,7 @@ namespace Quik.CommandMachine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Image(QuikTexture texture, ReadOnlySpan<QRectangle> rectangles, ReadOnlySpan<QRectangle> uvs)
|
public void Image(QImage texture, ReadOnlySpan<QRectangle> rectangles, ReadOnlySpan<QRectangle> uvs)
|
||||||
{
|
{
|
||||||
int count = Math.Min(rectangles.Length, uvs.Length);
|
int count = Math.Min(rectangles.Length, uvs.Length);
|
||||||
Enqueue(Command.Image);
|
Enqueue(Command.Image);
|
||||||
@@ -266,7 +283,7 @@ namespace Quik.CommandMachine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Image3D(QuikTexture texture, in Image3DCall call)
|
public void Image3D(QImage texture, in Image3DCall call)
|
||||||
{
|
{
|
||||||
Enqueue(Command.Image);
|
Enqueue(Command.Image);
|
||||||
Enqueue(new Frame(ImageCommandFlags.Image3d | ImageCommandFlags.Single));
|
Enqueue(new Frame(ImageCommandFlags.Image3d | ImageCommandFlags.Single));
|
||||||
@@ -276,7 +293,7 @@ namespace Quik.CommandMachine
|
|||||||
Enqueue(new Frame(call.Layer));
|
Enqueue(new Frame(call.Layer));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Image3D(QuikTexture texture, ReadOnlySpan<Image3DCall> calls)
|
public void Image3D(QImage texture, ReadOnlySpan<Image3DCall> calls)
|
||||||
{
|
{
|
||||||
Enqueue(Command.Image);
|
Enqueue(Command.Image);
|
||||||
Enqueue(new Frame((int)ImageCommandFlags.Image3d, calls.Length));
|
Enqueue(new Frame((int)ImageCommandFlags.Image3d, calls.Length));
|
||||||
@@ -298,6 +315,27 @@ namespace Quik.CommandMachine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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);
|
public CommandQueue GetEnumerator() => new CommandQueue(_frames);
|
||||||
IEnumerator<Frame> IEnumerable<Frame>.GetEnumerator() => GetEnumerator();
|
IEnumerator<Frame> IEnumerable<Frame>.GetEnumerator() => GetEnumerator();
|
||||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
@@ -354,6 +392,28 @@ namespace Quik.CommandMachine
|
|||||||
|
|
||||||
public Frame Peek() => TryPeek(out Frame frame) ? frame : throw new Exception("No more frames left.");
|
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()
|
public bool MoveNext()
|
||||||
{
|
{
|
||||||
if (_current + 1 < _frames.Count)
|
if (_current + 1 < _frames.Count)
|
||||||
@@ -364,6 +424,7 @@ namespace Quik.CommandMachine
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
_current = -1;
|
_current = -1;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Quik.CommandMachine
|
namespace Dashboard.CommandMachine
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Explicit)]
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
public struct Frame
|
public struct Frame
|
||||||
@@ -28,7 +28,7 @@ namespace Quik.CommandMachine
|
|||||||
private float _f4;
|
private float _f4;
|
||||||
|
|
||||||
[FieldOffset(24)]
|
[FieldOffset(24)]
|
||||||
private object _object;
|
private object? _object = null;
|
||||||
|
|
||||||
public bool IsCommand => _type == FrameType.Command;
|
public bool IsCommand => _type == FrameType.Command;
|
||||||
public bool IsInteger =>
|
public bool IsInteger =>
|
||||||
@@ -200,7 +200,7 @@ namespace Quik.CommandMachine
|
|||||||
|
|
||||||
public T As<T>()
|
public T As<T>()
|
||||||
{
|
{
|
||||||
return (T)_object;
|
return (T)_object!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float GetF(int i)
|
public float GetF(int i)
|
||||||
68
Dashboard/CommandMachine/FrameType.cs
Normal file
68
Dashboard/CommandMachine/FrameType.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Quik.CommandMachine
|
namespace Dashboard.CommandMachine
|
||||||
{
|
{
|
||||||
public enum ImageCommandFlags
|
public enum ImageCommandFlags
|
||||||
{
|
{
|
||||||
69
Dashboard/CommandMachine/Serializers.cs
Normal file
69
Dashboard/CommandMachine/Serializers.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
|
||||||
|
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
Dashboard/Controls/CommonEnums.cs
Normal file
56
Dashboard/Controls/CommonEnums.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
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
Dashboard/Controls/ContainerControl.cs
Normal file
50
Dashboard/Controls/ContainerControl.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
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
Dashboard/Controls/Control.cs
Normal file
125
Dashboard/Controls/Control.cs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
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
Dashboard/Controls/FlowBox.cs
Normal file
100
Dashboard/Controls/FlowBox.cs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
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
Dashboard/Controls/Label.cs
Normal file
31
Dashboard/Controls/Label.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
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
Dashboard/Controls/UIBase.cs
Normal file
102
Dashboard/Controls/UIBase.cs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Quik.Controls
|
namespace Dashboard.Controls
|
||||||
{
|
{
|
||||||
public class View : UIBase
|
public class View : UIBase
|
||||||
{
|
{
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Nullable>disable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>7.3</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Quik.Media.Color
|
namespace Dashboard.Media.Color
|
||||||
{
|
{
|
||||||
public static class FormatConvert
|
public static class FormatConvert
|
||||||
{
|
{
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Quik.Media.Color
|
namespace Dashboard.Media.Color
|
||||||
{
|
{
|
||||||
public class QImageBuffer : QImage
|
public class QImageBuffer : QImage
|
||||||
{
|
{
|
||||||
private byte[] buffer;
|
private byte[] buffer;
|
||||||
GCHandle handle;
|
GCHandle handle;
|
||||||
|
private bool isSdf = false;
|
||||||
|
|
||||||
public override QImageFormat InternalFormat { get; }
|
public override QImageFormat InternalFormat { get; }
|
||||||
public override int Width { get; }
|
public override int Width { get; }
|
||||||
public override int Height { get; }
|
public override int Height { get; }
|
||||||
public override int Depth { get; }
|
public override int Depth { get; }
|
||||||
|
public override bool IsSdf => isSdf;
|
||||||
|
|
||||||
public QImageBuffer(QImageFormat format, int width, int height, int depth = 1)
|
public QImageBuffer(QImageFormat format, int width, int height, int depth = 1)
|
||||||
{
|
{
|
||||||
@@ -29,7 +31,7 @@ namespace Quik.Media.Color
|
|||||||
|
|
||||||
private QImageLock Lock()
|
private QImageLock Lock()
|
||||||
{
|
{
|
||||||
handle.Free();
|
if (handle.IsAllocated) handle.Free();
|
||||||
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||||
IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
|
IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
|
||||||
return new QImageLock(InternalFormat, Width, Height, Depth, ptr);
|
return new QImageLock(InternalFormat, Width, Height, Depth, ptr);
|
||||||
@@ -37,8 +39,7 @@ namespace Quik.Media.Color
|
|||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
buffer = null;
|
if (handle.IsAllocated) handle.Free();
|
||||||
handle.Free();
|
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
@@ -62,5 +63,7 @@ namespace Quik.Media.Color
|
|||||||
{
|
{
|
||||||
handle.Free();
|
handle.Free();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetSdf(bool value = true) => isSdf = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Quik.Media.Color
|
namespace Dashboard.Media.Color
|
||||||
{
|
{
|
||||||
public unsafe struct LockIO
|
public unsafe struct LockIO
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Quik.Media
|
namespace Dashboard.Media
|
||||||
{
|
{
|
||||||
public static class Extensions
|
public static class Extensions
|
||||||
{
|
{
|
||||||
184
Dashboard/Media/Font/FontAtlas.cs
Normal file
184
Dashboard/Media/Font/FontAtlas.cs
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
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
Dashboard/Media/Font/FontFace.cs
Normal file
238
Dashboard/Media/Font/FontFace.cs
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
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
Dashboard/Media/Font/FontSlant.cs
Normal file
9
Dashboard/Media/Font/FontSlant.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Dashboard.Media.Font
|
||||||
|
{
|
||||||
|
public enum FontSlant
|
||||||
|
{
|
||||||
|
Normal = 0,
|
||||||
|
Italic = 1,
|
||||||
|
Oblique = 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Dashboard/Media/Font/FontStretch.cs
Normal file
18
Dashboard/Media/Font/FontStretch.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
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
Dashboard/Media/Font/FontWeight.cs
Normal file
22
Dashboard/Media/Font/FontWeight.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
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
Dashboard/Media/Font/SystemFontFamily.cs
Normal file
26
Dashboard/Media/Font/SystemFontFamily.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Quik.Media
|
namespace Dashboard.Media
|
||||||
{
|
{
|
||||||
public enum QImageFormat
|
public enum QImageFormat
|
||||||
{
|
{
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Quik.Media
|
namespace Dashboard.Media
|
||||||
{
|
{
|
||||||
public enum MediaHint
|
public enum MediaHint
|
||||||
{
|
{
|
||||||
129
Dashboard/Media/QFont.cs
Normal file
129
Dashboard/Media/QFont.cs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Quik.Media
|
namespace Dashboard.Media
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Glyph properties with metrics based on FreeType glyph metrics.
|
/// Glyph properties with metrics based on FreeType glyph metrics.
|
||||||
@@ -10,11 +10,6 @@ namespace Quik.Media
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int Rune { get; }
|
public int Rune { get; }
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// Location of the glyph on the atlas.
|
|
||||||
// /// </summary>
|
|
||||||
// public QRectangle Location { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Size of the glyph in units.
|
/// Size of the glyph in units.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -37,14 +32,12 @@ namespace Quik.Media
|
|||||||
|
|
||||||
public QGlyphMetrics(
|
public QGlyphMetrics(
|
||||||
int character,
|
int character,
|
||||||
// QRectangle location,
|
|
||||||
QVec2 size,
|
QVec2 size,
|
||||||
QVec2 horizontalBearing,
|
QVec2 horizontalBearing,
|
||||||
QVec2 verticalBearing,
|
QVec2 verticalBearing,
|
||||||
QVec2 advance)
|
QVec2 advance)
|
||||||
{
|
{
|
||||||
Rune = character;
|
Rune = character;
|
||||||
// Location = location;
|
|
||||||
Size = size;
|
Size = size;
|
||||||
HorizontalBearing = horizontalBearing;
|
HorizontalBearing = horizontalBearing;
|
||||||
VerticalBearing = verticalBearing;
|
VerticalBearing = verticalBearing;
|
||||||
120
Dashboard/Media/QImage.cs
Normal file
120
Dashboard/Media/QImage.cs
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Quik
|
namespace Dashboard
|
||||||
{
|
{
|
||||||
public enum MouseButton
|
public enum MouseButton : byte
|
||||||
{
|
{
|
||||||
Primary = 1 << 0,
|
Primary = 1 << 0,
|
||||||
Secondary = 1 << 1,
|
Secondary = 1 << 1,
|
||||||
@@ -11,7 +11,7 @@ namespace Quik
|
|||||||
Auxilliary2 = 1 << 4,
|
Auxilliary2 = 1 << 4,
|
||||||
Auxilliary3 = 1 << 5,
|
Auxilliary3 = 1 << 5,
|
||||||
Auxilliary4 = 1 << 6,
|
Auxilliary4 = 1 << 6,
|
||||||
Auxilliary5 = 1 << 8,
|
Auxilliary5 = 1 << 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct MouseState
|
public struct MouseState
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Quik.OpenGL
|
namespace Dashboard.OpenGL
|
||||||
{
|
{
|
||||||
public unsafe static partial class GL
|
public unsafe static partial class GL
|
||||||
{
|
{
|
||||||
private delegate void BufferDataProc(GLEnum target, int size, void* data, GLEnum usageHint);
|
private delegate void BufferDataProc(GLEnum target, int size, void* data, GLEnum usageHint);
|
||||||
|
|
||||||
private static GenObjectsProc _genBuffers;
|
private static GenObjectsProc? _genBuffers;
|
||||||
private static GenObjectsProc _deleteBuffers;
|
private static GenObjectsProc? _deleteBuffers;
|
||||||
private static BindSlottedProc _bindBuffer;
|
private static BindSlottedProc? _bindBuffer;
|
||||||
private static BufferDataProc _bufferData;
|
private static BufferDataProc? _bufferData;
|
||||||
|
|
||||||
private static void LoadBuffer()
|
private static void LoadBuffer()
|
||||||
{
|
{
|
||||||
@@ -24,7 +24,7 @@ namespace Quik.OpenGL
|
|||||||
public static void GenBuffers(int count, out int buffers)
|
public static void GenBuffers(int count, out int buffers)
|
||||||
{
|
{
|
||||||
fixed (int *ptr = &buffers)
|
fixed (int *ptr = &buffers)
|
||||||
_genBuffers(count, ptr);
|
_genBuffers!(count, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
@@ -41,7 +41,7 @@ namespace Quik.OpenGL
|
|||||||
public static void DeleteBuffers(int count, ref int buffers)
|
public static void DeleteBuffers(int count, ref int buffers)
|
||||||
{
|
{
|
||||||
fixed (int *ptr = &buffers)
|
fixed (int *ptr = &buffers)
|
||||||
_deleteBuffers(count, ptr);
|
_deleteBuffers!(count, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
@@ -53,19 +53,19 @@ namespace Quik.OpenGL
|
|||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void BindBuffer(GLEnum target, int buffer)
|
public static void BindBuffer(GLEnum target, int buffer)
|
||||||
{
|
{
|
||||||
_bindBuffer(target, buffer);
|
_bindBuffer!(target, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void BufferData(GLEnum target, int size, IntPtr data, GLEnum usageHint) =>
|
public static void BufferData(GLEnum target, int size, IntPtr data, GLEnum usageHint) =>
|
||||||
_bufferData(target, size, (void*)data, usageHint);
|
_bufferData!(target, size, (void*)data, usageHint);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void BufferData<T>(GLEnum target, int size, ref T data, GLEnum usageHint)
|
public static void BufferData<T>(GLEnum target, int size, ref T data, GLEnum usageHint)
|
||||||
where T : unmanaged
|
where T : unmanaged
|
||||||
{
|
{
|
||||||
fixed (T* ptr = &data)
|
fixed (T* ptr = &data)
|
||||||
_bufferData(target, size, ptr, usageHint);
|
_bufferData!(target, size, ptr, usageHint);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using static Quik.OpenGL.GLEnum;
|
using static Dashboard.OpenGL.GLEnum;
|
||||||
|
|
||||||
namespace Quik.OpenGL
|
namespace Dashboard.OpenGL
|
||||||
{
|
{
|
||||||
public unsafe static partial class GL
|
public unsafe static partial class GL
|
||||||
{
|
{
|
||||||
@@ -16,16 +16,16 @@ namespace Quik.OpenGL
|
|||||||
private delegate void DeleteProgramProc(int program);
|
private delegate void DeleteProgramProc(int program);
|
||||||
private delegate int GetShaderLocationProc(int program, byte *name);
|
private delegate int GetShaderLocationProc(int program, byte *name);
|
||||||
|
|
||||||
private static CreateProgramProc _createProgram;
|
private static CreateProgramProc? _createProgram;
|
||||||
private static UseProgramProc _useProgram;
|
private static UseProgramProc? _useProgram;
|
||||||
private static AttachShaderProc _attachShader;
|
private static AttachShaderProc? _attachShader;
|
||||||
private static DetachShaderProc _detachShader;
|
private static DetachShaderProc? _detachShader;
|
||||||
private static LinkProgramProc _linkProgram;
|
private static LinkProgramProc? _linkProgram;
|
||||||
private static GetProgramProc _getProgram;
|
private static GetProgramProc? _getProgram;
|
||||||
private static GetProgramInfoLogProc _getProgramInfoLog;
|
private static GetProgramInfoLogProc? _getProgramInfoLog;
|
||||||
private static DeleteProgramProc _deleteProgram;
|
private static DeleteProgramProc? _deleteProgram;
|
||||||
private static GetShaderLocationProc _getUniformLocation;
|
private static GetShaderLocationProc? _getUniformLocation;
|
||||||
private static GetShaderLocationProc _getAttribLocation;
|
private static GetShaderLocationProc? _getAttribLocation;
|
||||||
|
|
||||||
private static void LoadProgram()
|
private static void LoadProgram()
|
||||||
{
|
{
|
||||||
@@ -42,26 +42,26 @@ namespace Quik.OpenGL
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static int CreateProgram() => _createProgram();
|
public static int CreateProgram() => _createProgram!();
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void UseProgram(int program) => _useProgram(program);
|
public static void UseProgram(int program) => _useProgram!(program);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void AttachShader(int program, int shader) => _attachShader(program, shader);
|
public static void AttachShader(int program, int shader) => _attachShader!(program, shader);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void DetachShader(int program, int shader) => _detachShader(program, shader);
|
public static void DetachShader(int program, int shader) => _detachShader!(program, shader);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void LinkProgram(int program) => _linkProgram(program);
|
public static void LinkProgram(int program) => _linkProgram!(program);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void GetProgram(int program, GLEnum pname, out int value)
|
public static void GetProgram(int program, GLEnum pname, out int value)
|
||||||
{
|
{
|
||||||
value = default;
|
value = default;
|
||||||
fixed (int* ptr = &value)
|
fixed (int* ptr = &value)
|
||||||
_getProgram(program, pname, ptr);
|
_getProgram!(program, pname, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
@@ -71,26 +71,26 @@ namespace Quik.OpenGL
|
|||||||
byte[] infoLog = new byte[length];
|
byte[] infoLog = new byte[length];
|
||||||
|
|
||||||
fixed (byte *ptr = infoLog)
|
fixed (byte *ptr = infoLog)
|
||||||
_getProgramInfoLog(program, length, &length, ptr);
|
_getProgramInfoLog!(program, length, &length, ptr);
|
||||||
|
|
||||||
return Encoding.UTF8.GetString(infoLog);
|
return Encoding.UTF8.GetString(infoLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void DeleteProgram(int program) => _deleteProgram(program);
|
public static void DeleteProgram(int program) => _deleteProgram!(program);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static int GetUniformLocation(int program, string name)
|
public static int GetUniformLocation(int program, string name)
|
||||||
{
|
{
|
||||||
fixed(byte* ptr = Encoding.UTF8.GetBytes(name))
|
fixed(byte* ptr = Encoding.UTF8.GetBytes(name))
|
||||||
return _getUniformLocation(program, ptr);
|
return _getUniformLocation!(program, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static int GetAttribLocation(int program, string name)
|
public static int GetAttribLocation(int program, string name)
|
||||||
{
|
{
|
||||||
fixed(byte* ptr = Encoding.UTF8.GetBytes(name))
|
fixed(byte* ptr = Encoding.UTF8.GetBytes(name))
|
||||||
return _getAttribLocation(program, ptr);
|
return _getAttribLocation!(program, ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,9 +2,9 @@ using System;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using static Quik.OpenGL.GLEnum;
|
using static Dashboard.OpenGL.GLEnum;
|
||||||
|
|
||||||
namespace Quik.OpenGL
|
namespace Dashboard.OpenGL
|
||||||
{
|
{
|
||||||
public unsafe static partial class GL
|
public unsafe static partial class GL
|
||||||
{
|
{
|
||||||
@@ -15,12 +15,12 @@ namespace Quik.OpenGL
|
|||||||
private delegate void GetShaderInfoLogProc(int shader, int maxLength, int* length, byte* infoLog);
|
private delegate void GetShaderInfoLogProc(int shader, int maxLength, int* length, byte* infoLog);
|
||||||
private delegate void DeleteShaderProc(int id);
|
private delegate void DeleteShaderProc(int id);
|
||||||
|
|
||||||
private static CreateShaderProc _createShader;
|
private static CreateShaderProc? _createShader;
|
||||||
private static ShaderSourceProc _shaderSource;
|
private static ShaderSourceProc? _shaderSource;
|
||||||
private static CompileShaderProc _compileShader;
|
private static CompileShaderProc? _compileShader;
|
||||||
private static GetShaderProc _getShader;
|
private static GetShaderProc? _getShader;
|
||||||
private static GetShaderInfoLogProc _getShaderInfoLog;
|
private static GetShaderInfoLogProc? _getShaderInfoLog;
|
||||||
private static DeleteShaderProc _deleteShader;
|
private static DeleteShaderProc? _deleteShader;
|
||||||
|
|
||||||
private static void LoadShader()
|
private static void LoadShader()
|
||||||
{
|
{
|
||||||
@@ -33,7 +33,7 @@ namespace Quik.OpenGL
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static int CreateShader(GLEnum type) => _createShader(type);
|
public static int CreateShader(GLEnum type) => _createShader!(type);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void ShaderSource(int shader, string source)
|
public static void ShaderSource(int shader, string source)
|
||||||
@@ -43,7 +43,7 @@ namespace Quik.OpenGL
|
|||||||
|
|
||||||
fixed (byte* ptr = &sourceUTF8[0])
|
fixed (byte* ptr = &sourceUTF8[0])
|
||||||
{
|
{
|
||||||
_shaderSource(shader, 1, &ptr, &length);
|
_shaderSource!(shader, 1, &ptr, &length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ namespace Quik.OpenGL
|
|||||||
fixed (byte** ptr = &pointers[0])
|
fixed (byte** ptr = &pointers[0])
|
||||||
fixed (int * len = &lengths[0])
|
fixed (int * len = &lengths[0])
|
||||||
{
|
{
|
||||||
_shaderSource(shader, count, ptr, len);
|
_shaderSource!(shader, count, ptr, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -82,14 +82,14 @@ namespace Quik.OpenGL
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void CompileShader(int shader) => _compileShader(shader);
|
public static void CompileShader(int shader) => _compileShader!(shader);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void GetShader(int shader, GLEnum pname, out int value)
|
public static void GetShader(int shader, GLEnum pname, out int value)
|
||||||
{
|
{
|
||||||
value = default;
|
value = default;
|
||||||
fixed (int *ptr = &value)
|
fixed (int *ptr = &value)
|
||||||
_getShader(shader, pname, ptr);
|
_getShader!(shader, pname, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
@@ -99,12 +99,12 @@ namespace Quik.OpenGL
|
|||||||
byte[] infoLog = new byte[length];
|
byte[] infoLog = new byte[length];
|
||||||
|
|
||||||
fixed (byte *ptr = infoLog)
|
fixed (byte *ptr = infoLog)
|
||||||
_getShaderInfoLog(shader, length, &length, ptr);
|
_getShaderInfoLog!(shader, length, &length, ptr);
|
||||||
|
|
||||||
return Encoding.UTF8.GetString(infoLog);
|
return Encoding.UTF8.GetString(infoLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void DeleteShader(int shader) => _deleteShader(shader);
|
public static void DeleteShader(int shader) => _deleteShader!(shader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Quik.OpenGL
|
namespace Dashboard.OpenGL
|
||||||
{
|
{
|
||||||
public unsafe static partial class GL
|
public unsafe static partial class GL
|
||||||
{
|
{
|
||||||
@@ -9,26 +9,30 @@ namespace Quik.OpenGL
|
|||||||
private delegate void PixelStoreiProc(GLEnum pname, int param);
|
private delegate void PixelStoreiProc(GLEnum pname, int param);
|
||||||
private delegate void PixelStorefProc(GLEnum pname, float 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 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 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 TexParameteriProc(GLEnum target, GLEnum pname, int value);
|
||||||
private delegate void TexParameterfProc(GLEnum target, GLEnum pname, float value);
|
private delegate void TexParameterfProc(GLEnum target, GLEnum pname, float value);
|
||||||
private delegate void TexParameterivProc(GLEnum target, GLEnum pname, int* value);
|
private delegate void TexParameterivProc(GLEnum target, GLEnum pname, int* value);
|
||||||
private delegate void TexParameterfvProc(GLEnum target, GLEnum pname, float* value);
|
private delegate void TexParameterfvProc(GLEnum target, GLEnum pname, float* value);
|
||||||
private delegate void GenerateMipmapProc(GLEnum target);
|
private delegate void GenerateMipmapProc(GLEnum target);
|
||||||
|
|
||||||
private static GenObjectsProc _genTextures;
|
private static GenObjectsProc? _genTextures;
|
||||||
private static GenObjectsProc _deleteTextures;
|
private static GenObjectsProc? _deleteTextures;
|
||||||
private static BindSlottedProc _bindTexture;
|
private static BindSlottedProc? _bindTexture;
|
||||||
private static ActiveTextureProc _activeTexture;
|
private static ActiveTextureProc? _activeTexture;
|
||||||
private static PixelStoreiProc _pixelStorei;
|
private static PixelStoreiProc? _pixelStorei;
|
||||||
private static PixelStorefProc _pixelStoref;
|
private static PixelStorefProc? _pixelStoref;
|
||||||
private static TexImage2DProc _texImage2D;
|
private static TexImage2DProc? _texImage2D;
|
||||||
private static TexSubImage2DProc _texSubImage2D;
|
private static TexImage3DProc? _texImage3D;
|
||||||
private static TexParameteriProc _texParameteri;
|
private static TexSubImage2DProc? _texSubImage2D;
|
||||||
private static TexParameterfProc _texParameterf;
|
private static TexSubImage3DProc? _texSubImage3D;
|
||||||
private static TexParameterivProc _texParameteriv;
|
private static TexParameteriProc? _texParameteri;
|
||||||
private static TexParameterfvProc _texParameterfv;
|
private static TexParameterfProc? _texParameterf;
|
||||||
private static GenerateMipmapProc _generateMipmap;
|
private static TexParameterivProc? _texParameteriv;
|
||||||
|
private static TexParameterfvProc? _texParameterfv;
|
||||||
|
private static GenerateMipmapProc? _generateMipmap;
|
||||||
|
|
||||||
private static void LoadTexture()
|
private static void LoadTexture()
|
||||||
{
|
{
|
||||||
@@ -39,7 +43,9 @@ namespace Quik.OpenGL
|
|||||||
_pixelStorei = GetProcAddress<PixelStoreiProc>("glPixelStorei");
|
_pixelStorei = GetProcAddress<PixelStoreiProc>("glPixelStorei");
|
||||||
_pixelStoref = GetProcAddress<PixelStorefProc>("glPixelStoref");
|
_pixelStoref = GetProcAddress<PixelStorefProc>("glPixelStoref");
|
||||||
_texImage2D = GetProcAddress<TexImage2DProc>("glTexImage2D");
|
_texImage2D = GetProcAddress<TexImage2DProc>("glTexImage2D");
|
||||||
|
_texImage3D = GetProcAddress<TexImage3DProc>("glTexImage3D");
|
||||||
_texSubImage2D = GetProcAddress<TexSubImage2DProc>("glTexSubImage2D");
|
_texSubImage2D = GetProcAddress<TexSubImage2DProc>("glTexSubImage2D");
|
||||||
|
_texSubImage3D = GetProcAddress<TexSubImage3DProc>("glTexSubImage3D");
|
||||||
_texParameteri = GetProcAddress<TexParameteriProc>("glTexParameteri");
|
_texParameteri = GetProcAddress<TexParameteriProc>("glTexParameteri");
|
||||||
_texParameterf = GetProcAddress<TexParameterfProc>("glTexParameterf");
|
_texParameterf = GetProcAddress<TexParameterfProc>("glTexParameterf");
|
||||||
_texParameteriv = GetProcAddress<TexParameterivProc>("glTexParameteriv");
|
_texParameteriv = GetProcAddress<TexParameterivProc>("glTexParameteriv");
|
||||||
@@ -51,7 +57,7 @@ namespace Quik.OpenGL
|
|||||||
public static void GenTextures(int count, out int textures)
|
public static void GenTextures(int count, out int textures)
|
||||||
{
|
{
|
||||||
fixed (int *ptr = &textures)
|
fixed (int *ptr = &textures)
|
||||||
_genTextures(count, ptr);
|
_genTextures!(count, ptr);
|
||||||
}
|
}
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static int GenTexture()
|
public static int GenTexture()
|
||||||
@@ -66,7 +72,7 @@ namespace Quik.OpenGL
|
|||||||
public static void DeleteTextures(int count, in int textures)
|
public static void DeleteTextures(int count, in int textures)
|
||||||
{
|
{
|
||||||
fixed (int* ptr = &textures)
|
fixed (int* ptr = &textures)
|
||||||
_deleteTextures(count, ptr);
|
_deleteTextures!(count, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
@@ -76,41 +82,58 @@ namespace Quik.OpenGL
|
|||||||
public static void DeleteTextures(int[] textures) => DeleteTextures(textures.Length, in textures[0]);
|
public static void DeleteTextures(int[] textures) => DeleteTextures(textures.Length, in textures[0]);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void BindTexture(GLEnum target, int texture) => _bindTexture(target, texture);
|
public static void BindTexture(GLEnum target, int texture) => _bindTexture!(target, texture);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void ActiveTexture(GLEnum unit) => _activeTexture(unit);
|
public static void ActiveTexture(GLEnum unit) => _activeTexture!(unit);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void PixelStore(GLEnum pname, int value) => _pixelStorei(pname, value);
|
public static void PixelStore(GLEnum pname, int value) => _pixelStorei!(pname, value);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void PixelStore(GLEnum pname, float value) => _pixelStoref(pname, value);
|
public static void PixelStore(GLEnum pname, float value) => _pixelStoref!(pname, value);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void TexImage2D(GLEnum target, int level, GLEnum internalFormat, int width, int height, int border, GLEnum format, GLEnum pixelType, IntPtr data) =>
|
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);
|
_texImage2D!(target, level, internalFormat, width, height, border, format, pixelType, (void*)data);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[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
|
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)
|
fixed(T *ptr = &data)
|
||||||
_texImage2D(target, level, internalFormat, width, height, border, format, pixelType, ptr);
|
_texImage2D!(target, level, internalFormat, width, height, border, format, pixelType, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[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 =>
|
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<T>(target, level, internalFormat, width, height, border, format, pixelType, in data[0]);
|
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)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void TexSubImage2D(GLEnum target, int level, int x, int y, int width, int height, GLEnum format, GLEnum pixelType, IntPtr data) =>
|
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);
|
_texSubImage2D!(target, level, x, y, width, height,format, pixelType, (void*)data);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[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
|
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)
|
fixed(T *ptr = &data)
|
||||||
_texSubImage2D(target, level, x, y, width, height, format, pixelType, ptr);
|
_texSubImage2D!(target, level, x, y, width, height, format, pixelType, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
@@ -118,16 +141,33 @@ namespace Quik.OpenGL
|
|||||||
TexSubImage2d<T>(target, level, x, y, width, height, format, pixelType, in data[0]);
|
TexSubImage2d<T>(target, level, x, y, width, height, format, pixelType, in data[0]);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void TexParameter(GLEnum target, GLEnum pname, int value) => _texParameteri(target, pname, value);
|
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)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void TexParameter(GLEnum target, GLEnum pname, GLEnum value) => _texParameteri(target, pname, (int)value);
|
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)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void TexParameter(GLEnum target, GLEnum pname, float value) => _texParameterf(target, pname, value);
|
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)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void TexParameter(GLEnum target, GLEnum pname, ref int values)
|
public static void TexParameter(GLEnum target, GLEnum pname, ref int values)
|
||||||
{
|
{
|
||||||
fixed (int *ptr = &values)
|
fixed (int *ptr = &values)
|
||||||
_texParameteriv(target, pname, ptr);
|
_texParameteriv!(target, pname, ptr);
|
||||||
}
|
}
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void TexParameter(GLEnum target, GLEnum pname, int[] values) => TexParameter(target, pname, ref values[0]);
|
public static void TexParameter(GLEnum target, GLEnum pname, int[] values) => TexParameter(target, pname, ref values[0]);
|
||||||
@@ -136,12 +176,12 @@ namespace Quik.OpenGL
|
|||||||
public static void TexParameter(GLEnum target, GLEnum pname, ref float values)
|
public static void TexParameter(GLEnum target, GLEnum pname, ref float values)
|
||||||
{
|
{
|
||||||
fixed (float *ptr = &values)
|
fixed (float *ptr = &values)
|
||||||
_texParameterfv(target, pname, ptr);
|
_texParameterfv!(target, pname, ptr);
|
||||||
}
|
}
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void TexParameter(GLEnum target, GLEnum pname, float[] values) => TexParameter(target, pname, ref values[0]);
|
public static void TexParameter(GLEnum target, GLEnum pname, float[] values) => TexParameter(target, pname, ref values[0]);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void GenerateMipmap(GLEnum target) => _generateMipmap(target);
|
public static void GenerateMipmap(GLEnum target) => _generateMipmap!(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Quik.OpenGL
|
namespace Dashboard.OpenGL
|
||||||
{
|
{
|
||||||
public unsafe static partial class GL
|
public unsafe static partial class GL
|
||||||
{
|
{
|
||||||
@@ -16,25 +16,25 @@ namespace Quik.OpenGL
|
|||||||
private delegate void UniformNivProc(int location, int count, int* value);
|
private delegate void UniformNivProc(int location, int count, int* value);
|
||||||
private delegate void UniformMatrixNxNfvProc(int location, int count, bool transpose, float *value);
|
private delegate void UniformMatrixNxNfvProc(int location, int count, bool transpose, float *value);
|
||||||
|
|
||||||
private static Uniform1fProc _uniform1f;
|
private static Uniform1fProc? _uniform1f;
|
||||||
private static Uniform2fProc _uniform2f;
|
private static Uniform2fProc? _uniform2f;
|
||||||
private static Uniform3fProc _uniform3f;
|
private static Uniform3fProc? _uniform3f;
|
||||||
private static Uniform4fProc _uniform4f;
|
private static Uniform4fProc? _uniform4f;
|
||||||
private static Uniform1iProc _uniform1i;
|
private static Uniform1iProc? _uniform1i;
|
||||||
private static Uniform2iProc _uniform2i;
|
private static Uniform2iProc? _uniform2i;
|
||||||
private static Uniform3iProc _uniform3i;
|
private static Uniform3iProc? _uniform3i;
|
||||||
private static Uniform4iProc _uniform4i;
|
private static Uniform4iProc? _uniform4i;
|
||||||
private static UniformNfvProc _uniform1fv;
|
private static UniformNfvProc? _uniform1fv;
|
||||||
private static UniformNfvProc _uniform2fv;
|
private static UniformNfvProc? _uniform2fv;
|
||||||
private static UniformNfvProc _uniform3fv;
|
private static UniformNfvProc? _uniform3fv;
|
||||||
private static UniformNfvProc _uniform4fv;
|
private static UniformNfvProc? _uniform4fv;
|
||||||
private static UniformNivProc _uniform1iv;
|
private static UniformNivProc? _uniform1iv;
|
||||||
private static UniformNivProc _uniform2iv;
|
private static UniformNivProc? _uniform2iv;
|
||||||
private static UniformNivProc _uniform3iv;
|
private static UniformNivProc? _uniform3iv;
|
||||||
private static UniformNivProc _uniform4iv;
|
private static UniformNivProc? _uniform4iv;
|
||||||
private static UniformMatrixNxNfvProc _uniformMatrix2fv;
|
private static UniformMatrixNxNfvProc? _uniformMatrix2fv;
|
||||||
private static UniformMatrixNxNfvProc _uniformMatrix3fv;
|
private static UniformMatrixNxNfvProc? _uniformMatrix3fv;
|
||||||
private static UniformMatrixNxNfvProc _uniformMatrix4fv;
|
private static UniformMatrixNxNfvProc? _uniformMatrix4fv;
|
||||||
|
|
||||||
public static void LoadUniform()
|
public static void LoadUniform()
|
||||||
{
|
{
|
||||||
@@ -62,161 +62,161 @@ namespace Quik.OpenGL
|
|||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Uniform1(int location, float x)
|
public static void Uniform1(int location, float x)
|
||||||
{
|
{
|
||||||
_uniform1f(location, x);
|
_uniform1f!(location, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Uniform2(int location, float x, float y)
|
public static void Uniform2(int location, float x, float y)
|
||||||
{
|
{
|
||||||
_uniform2f(location, x, y);
|
_uniform2f!(location, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Uniform3(int location, float x, float y, float z)
|
public static void Uniform3(int location, float x, float y, float z)
|
||||||
{
|
{
|
||||||
_uniform3f(location, x, y, z);
|
_uniform3f!(location, x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Uniform4(int location, float x, float y, float z, float w)
|
public static void Uniform4(int location, float x, float y, float z, float w)
|
||||||
{
|
{
|
||||||
_uniform4f(location, x, y, z, w);
|
_uniform4f!(location, x, y, z, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Uniform1(int location, int x)
|
public static void Uniform1(int location, int x)
|
||||||
{
|
{
|
||||||
_uniform1i(location, x);
|
_uniform1i!(location, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Uniform2(int location, int x, int y)
|
public static void Uniform2(int location, int x, int y)
|
||||||
{
|
{
|
||||||
_uniform2i(location, x, y);
|
_uniform2i!(location, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Uniform3(int location, int x, int y, int z)
|
public static void Uniform3(int location, int x, int y, int z)
|
||||||
{
|
{
|
||||||
_uniform3i(location, x, y, z);
|
_uniform3i!(location, x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Uniform4(int location, int x, int y, int z, int w)
|
public static void Uniform4(int location, int x, int y, int z, int w)
|
||||||
{
|
{
|
||||||
_uniform4i(location, x, y, z, w);
|
_uniform4i!(location, x, y, z, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Uniform1(int location, int count, ref float first)
|
public static void Uniform1(int location, int count, ref float first)
|
||||||
{
|
{
|
||||||
fixed(float *ptr = &first)
|
fixed(float *ptr = &first)
|
||||||
_uniform1fv(location, count, ptr);
|
_uniform1fv!(location, count, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Uniform2(int location, int count, ref float first)
|
public static void Uniform2(int location, int count, ref float first)
|
||||||
{
|
{
|
||||||
fixed(float *ptr = &first)
|
fixed(float *ptr = &first)
|
||||||
_uniform2fv(location, count, ptr);
|
_uniform2fv!(location, count, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Uniform3(int location, int count, ref float first)
|
public static void Uniform3(int location, int count, ref float first)
|
||||||
{
|
{
|
||||||
fixed(float *ptr = &first)
|
fixed(float *ptr = &first)
|
||||||
_uniform3fv(location, count, ptr);
|
_uniform3fv!(location, count, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Uniform4(int location, int count, ref float first)
|
public static void Uniform4(int location, int count, ref float first)
|
||||||
{
|
{
|
||||||
fixed(float *ptr = &first)
|
fixed(float *ptr = &first)
|
||||||
_uniform4fv(location, count, ptr);
|
_uniform4fv!(location, count, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Uniform1(int location, int count, ref int first)
|
public static void Uniform1(int location, int count, ref int first)
|
||||||
{
|
{
|
||||||
fixed(int *ptr = &first)
|
fixed(int *ptr = &first)
|
||||||
_uniform1iv(location, count, ptr);
|
_uniform1iv!(location, count, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Uniform2(int location, int count, ref int first)
|
public static void Uniform2(int location, int count, ref int first)
|
||||||
{
|
{
|
||||||
fixed(int *ptr = &first)
|
fixed(int *ptr = &first)
|
||||||
_uniform2iv(location, count, ptr);
|
_uniform2iv!(location, count, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Uniform3(int location, int count, ref int first)
|
public static void Uniform3(int location, int count, ref int first)
|
||||||
{
|
{
|
||||||
fixed(int *ptr = &first)
|
fixed(int *ptr = &first)
|
||||||
_uniform3iv(location, count, ptr);
|
_uniform3iv!(location, count, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Uniform4(int location, int count, ref int first)
|
public static void Uniform4(int location, int count, ref int first)
|
||||||
{
|
{
|
||||||
fixed(int *ptr = &first)
|
fixed(int *ptr = &first)
|
||||||
_uniform4iv(location, count, ptr);
|
_uniform4iv!(location, count, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void UniformMatrix2(int location, bool transpose, ref float m11)
|
public static void UniformMatrix2(int location, bool transpose, ref float m11)
|
||||||
{
|
{
|
||||||
fixed (float* ptr = &m11)
|
fixed (float* ptr = &m11)
|
||||||
_uniformMatrix2fv(location, 1, transpose, ptr);
|
_uniformMatrix2fv!(location, 1, transpose, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void UniformMatrix3(int location, bool transpose, ref float m11)
|
public static void UniformMatrix3(int location, bool transpose, ref float m11)
|
||||||
{
|
{
|
||||||
fixed (float* ptr = &m11)
|
fixed (float* ptr = &m11)
|
||||||
_uniformMatrix3fv(location, 1, transpose, ptr);
|
_uniformMatrix3fv!(location, 1, transpose, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void UniformMatrix4(int location, bool transpose, ref float m11)
|
public static void UniformMatrix4(int location, bool transpose, ref float m11)
|
||||||
{
|
{
|
||||||
fixed (float* ptr = &m11)
|
fixed (float* ptr = &m11)
|
||||||
_uniformMatrix4fv(location, 1, transpose, ptr);
|
_uniformMatrix4fv!(location, 1, transpose, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void UniformMatrix2(int location, int count, bool transpose, ref float m11)
|
public static void UniformMatrix2(int location, int count, bool transpose, ref float m11)
|
||||||
{
|
{
|
||||||
fixed (float* ptr = &m11)
|
fixed (float* ptr = &m11)
|
||||||
_uniformMatrix2fv(location, count, transpose, ptr);
|
_uniformMatrix2fv!(location, count, transpose, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void UniformMatrix3(int location, int count, bool transpose, ref float m11)
|
public static void UniformMatrix3(int location, int count, bool transpose, ref float m11)
|
||||||
{
|
{
|
||||||
fixed (float* ptr = &m11)
|
fixed (float* ptr = &m11)
|
||||||
_uniformMatrix3fv(location, count, transpose, ptr);
|
_uniformMatrix3fv!(location, count, transpose, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void UniformMatrix4(int location, int count, bool transpose, ref float m11)
|
public static void UniformMatrix4(int location, int count, bool transpose, ref float m11)
|
||||||
{
|
{
|
||||||
fixed (float* ptr = &m11)
|
fixed (float* ptr = &m11)
|
||||||
_uniformMatrix4fv(location, count, transpose, ptr);
|
_uniformMatrix4fv!(location, count, transpose, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void UniformMatrix4(int location, bool transpose, in QMat4 m4)
|
public static void UniformMatrix4(int location, bool transpose, in QMat4 m4)
|
||||||
{
|
{
|
||||||
fixed (float* ptr = &m4.M11)
|
fixed (float* ptr = &m4.M11)
|
||||||
_uniformMatrix4fv(location, 1, transpose, ptr);
|
_uniformMatrix4fv!(location, 1, transpose, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void UniformMatrix4(int location, int count, bool transpose, ref QMat4 m4)
|
public static void UniformMatrix4(int location, int count, bool transpose, ref QMat4 m4)
|
||||||
{
|
{
|
||||||
fixed (float* ptr = &m4.M11)
|
fixed (float* ptr = &m4.M11)
|
||||||
_uniformMatrix4fv(location, count, transpose, ptr);
|
_uniformMatrix4fv!(location, count, transpose, ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Quik.OpenGL
|
namespace Dashboard.OpenGL
|
||||||
{
|
{
|
||||||
public unsafe static partial class GL
|
public unsafe static partial class GL
|
||||||
{
|
{
|
||||||
@@ -9,13 +9,13 @@ namespace Quik.OpenGL
|
|||||||
private delegate void VertexAttribPointerProc(int location, int size, GLEnum type, bool normalized, int stride, IntPtr offset);
|
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 delegate void VertexAttribIPointerProc(int location, int size, GLEnum type, int stride, IntPtr offset);
|
||||||
|
|
||||||
private static GenObjectsProc _genVertexArrays;
|
private static GenObjectsProc? _genVertexArrays;
|
||||||
private static GenObjectsProc _deleteVertexArrays;
|
private static GenObjectsProc? _deleteVertexArrays;
|
||||||
private static BindObjectProc _bindVertexArray;
|
private static BindObjectProc? _bindVertexArray;
|
||||||
private static EnableVertexAttribArrayProc _enableVertexAttribArray;
|
private static EnableVertexAttribArrayProc? _enableVertexAttribArray;
|
||||||
private static EnableVertexAttribArrayProc _disableVertexAttribArray;
|
private static EnableVertexAttribArrayProc? _disableVertexAttribArray;
|
||||||
private static VertexAttribPointerProc _vertexAttribPointer;
|
private static VertexAttribPointerProc? _vertexAttribPointer;
|
||||||
private static VertexAttribIPointerProc _vertexAttribIPointer;
|
private static VertexAttribIPointerProc? _vertexAttribIPointer;
|
||||||
|
|
||||||
private static void LoadVertexArrays()
|
private static void LoadVertexArrays()
|
||||||
{
|
{
|
||||||
@@ -32,7 +32,7 @@ namespace Quik.OpenGL
|
|||||||
public static void GenVertexArrays(int count, out int vertexArrays)
|
public static void GenVertexArrays(int count, out int vertexArrays)
|
||||||
{
|
{
|
||||||
fixed (int *ptr = &vertexArrays)
|
fixed (int *ptr = &vertexArrays)
|
||||||
_genVertexArrays(count, ptr);
|
_genVertexArrays!(count, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
@@ -49,7 +49,7 @@ namespace Quik.OpenGL
|
|||||||
public static void DeleteVertexArrays(int count, ref int vertexArrays)
|
public static void DeleteVertexArrays(int count, ref int vertexArrays)
|
||||||
{
|
{
|
||||||
fixed (int *ptr = &vertexArrays)
|
fixed (int *ptr = &vertexArrays)
|
||||||
_deleteVertexArrays(count, ptr);
|
_deleteVertexArrays!(count, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
@@ -59,20 +59,20 @@ namespace Quik.OpenGL
|
|||||||
public static void DeleteVertexArray(int vertexArray) => DeleteVertexArrays(1, ref vertexArray);
|
public static void DeleteVertexArray(int vertexArray) => DeleteVertexArrays(1, ref vertexArray);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void BindVertexArray(int vertexArray) => _bindVertexArray(vertexArray);
|
public static void BindVertexArray(int vertexArray) => _bindVertexArray!(vertexArray);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void EnableVertexAttribArray(int location) => _enableVertexAttribArray(location);
|
public static void EnableVertexAttribArray(int location) => _enableVertexAttribArray!(location);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void DisableVertexAttribArray(int location) => _disableVertexAttribArray(location);
|
public static void DisableVertexAttribArray(int location) => _disableVertexAttribArray!(location);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void VertexAttribPointer(int location, int size, GLEnum type, bool normalized, int stride, int offset) =>
|
public static void VertexAttribPointer(int location, int size, GLEnum type, bool normalized, int stride, int offset) =>
|
||||||
_vertexAttribPointer(location, size, type, normalized, stride, (IntPtr)offset);
|
_vertexAttribPointer!(location, size, type, normalized, stride, (IntPtr)offset);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void VertexAttribIPointer(int location, int size, GLEnum type, int stride, int offset) =>
|
public static void VertexAttribIPointer(int location, int size, GLEnum type, int stride, int offset) =>
|
||||||
_vertexAttribIPointer(location, size, type, stride, (IntPtr)offset);
|
_vertexAttribIPointer!(location, size, type, stride, (IntPtr)offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ using System;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Quik.OpenGL
|
namespace Dashboard.OpenGL
|
||||||
{
|
{
|
||||||
public delegate IntPtr GetProcAddressProc(string procName);
|
public delegate IntPtr GetProcAddressProc(string procName);
|
||||||
|
|
||||||
@@ -23,24 +23,25 @@ namespace Quik.OpenGL
|
|||||||
|
|
||||||
private const short AggressiveInlining = (short)MethodImplOptions.AggressiveInlining;
|
private const short AggressiveInlining = (short)MethodImplOptions.AggressiveInlining;
|
||||||
|
|
||||||
private static GetProcAddressProc _getProcAddress;
|
private static GetProcAddressProc? _getProcAddress;
|
||||||
private static GLEnum1Proc _enable;
|
private static GLEnum1Proc? _enable;
|
||||||
private static GLEnum1Proc _disable;
|
private static GLEnum1Proc? _disable;
|
||||||
private static GLEnum2Proc _blendFunc;
|
private static GLEnum2Proc? _blendFunc;
|
||||||
private static GLEnum1Proc _depthFunc;
|
private static GLEnum1Proc? _depthFunc;
|
||||||
private static GLEnum1Proc _clear;
|
private static GLEnum1Proc? _clear;
|
||||||
private static GLI4Proc _viewport;
|
private static GLI4Proc? _viewport;
|
||||||
private static GLF4Proc _clearColor;
|
private static GLI4Proc? _scissor;
|
||||||
private static DrawElementsProc _drawElements;
|
private static GLF4Proc? _clearColor;
|
||||||
private static DrawArraysProc _drawArrays;
|
private static DrawElementsProc? _drawElements;
|
||||||
private static GetIntegervProc _getIntegerv;
|
private static DrawArraysProc? _drawArrays;
|
||||||
private static GetFloatvProc _getFloatv;
|
private static GetIntegervProc? _getIntegerv;
|
||||||
private static GetStringProc _getString;
|
private static GetFloatvProc? _getFloatv;
|
||||||
|
private static GetStringProc? _getString;
|
||||||
|
|
||||||
private static T GetProcAddress<T>(string procName)
|
private static T GetProcAddress<T>(string procName)
|
||||||
where T : Delegate
|
where T : Delegate
|
||||||
{
|
{
|
||||||
IntPtr funcptr = _getProcAddress(procName);
|
IntPtr funcptr = _getProcAddress!(procName);
|
||||||
return Marshal.GetDelegateForFunctionPointer<T>(funcptr);
|
return Marshal.GetDelegateForFunctionPointer<T>(funcptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +55,7 @@ namespace Quik.OpenGL
|
|||||||
_depthFunc = GetProcAddress<GLEnum1Proc>("glDepthFunc");
|
_depthFunc = GetProcAddress<GLEnum1Proc>("glDepthFunc");
|
||||||
_clear = GetProcAddress<GLEnum1Proc>("glClear");
|
_clear = GetProcAddress<GLEnum1Proc>("glClear");
|
||||||
_viewport = GetProcAddress<GLI4Proc>("glViewport");
|
_viewport = GetProcAddress<GLI4Proc>("glViewport");
|
||||||
|
_scissor = GetProcAddress<GLI4Proc>("glScissor");
|
||||||
_clearColor = GetProcAddress<GLF4Proc>("glClearColor");
|
_clearColor = GetProcAddress<GLF4Proc>("glClearColor");
|
||||||
_drawElements = GetProcAddress<DrawElementsProc>("glDrawElements");
|
_drawElements = GetProcAddress<DrawElementsProc>("glDrawElements");
|
||||||
_drawArrays = GetProcAddress<DrawArraysProc>("glDrawArrays");
|
_drawArrays = GetProcAddress<DrawArraysProc>("glDrawArrays");
|
||||||
@@ -70,25 +72,27 @@ namespace Quik.OpenGL
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Enable(GLEnum cap) => _enable(cap);
|
public static void Enable(GLEnum cap) => _enable!(cap);
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Disable(GLEnum cap) => _disable(cap);
|
public static void Disable(GLEnum cap) => _disable!(cap);
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void BlendFunc(GLEnum src, GLEnum dst) => _blendFunc(src, dst);
|
public static void BlendFunc(GLEnum src, GLEnum dst) => _blendFunc!(src, dst);
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void DepthFunc(GLEnum func) => _depthFunc(func);
|
public static void DepthFunc(GLEnum func) => _depthFunc!(func);
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Clear(GLEnum buffer_bits) => _clear(buffer_bits);
|
public static void Clear(GLEnum buffer_bits) => _clear!(buffer_bits);
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Viewport(int x, int y, int w, int h) => _viewport(x, y, w, h);
|
public static void Viewport(int x, int y, int w, int h) => _viewport!(x, y, w, h);
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void ClearColor(float r, float g, float b, float a) => _clearColor(r, g, b, a);
|
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)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void DrawElements(GLEnum primitive, int count, GLEnum type, int offset) => _drawElements(primitive, count, type, (void*)offset);
|
public static void DrawElements(GLEnum primitive, int count, GLEnum type, int offset) => _drawElements!(primitive, count, type, (void*)offset);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void DrawArrays(GLEnum primitive, int offset, int count) => _drawArrays(primitive, offset, count);
|
public static void DrawArrays(GLEnum primitive, int offset, int count) => _drawArrays!(primitive, offset, count);
|
||||||
|
|
||||||
[MethodImpl(AggressiveInlining)]
|
[MethodImpl(AggressiveInlining)]
|
||||||
public static void Get(GLEnum pname, out int value)
|
public static void Get(GLEnum pname, out int value)
|
||||||
@@ -96,7 +100,7 @@ namespace Quik.OpenGL
|
|||||||
value = default;
|
value = default;
|
||||||
fixed(int* ptr = &value)
|
fixed(int* ptr = &value)
|
||||||
{
|
{
|
||||||
_getIntegerv(pname, ptr);
|
_getIntegerv!(pname, ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +110,7 @@ namespace Quik.OpenGL
|
|||||||
value = default;
|
value = default;
|
||||||
fixed (float* ptr = &value)
|
fixed (float* ptr = &value)
|
||||||
{
|
{
|
||||||
_getFloatv(pname, ptr);
|
_getFloatv!(pname, ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +118,7 @@ namespace Quik.OpenGL
|
|||||||
public static string GetString(GLEnum pname)
|
public static string GetString(GLEnum pname)
|
||||||
{
|
{
|
||||||
int length;
|
int length;
|
||||||
byte* str = _getString(pname);
|
byte* str = _getString!(pname);
|
||||||
|
|
||||||
for (length = 0; str[length] == 0 || length < 256; length++);
|
for (length = 0; str[length] == 0 || length < 256; length++);
|
||||||
|
|
||||||
@@ -1,29 +1,37 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Quik.VertexGenerator;
|
using Dashboard.VertexGenerator;
|
||||||
using static Quik.OpenGL.GLEnum;
|
using static Dashboard.OpenGL.GLEnum;
|
||||||
|
using Dashboard.Media;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Quik.OpenGL
|
namespace Dashboard.OpenGL
|
||||||
{
|
{
|
||||||
public class GL21Driver : IDisposable
|
public class GL21Driver : IDisposable
|
||||||
{
|
{
|
||||||
private int program;
|
private int program;
|
||||||
private int v2Position;
|
private int v2Position;
|
||||||
private int iZIndex;
|
private int fZIndex;
|
||||||
private int v2TexPos;
|
private int v2TexPos;
|
||||||
|
private int fTexLayer;
|
||||||
private int v4Color;
|
private int v4Color;
|
||||||
private int m4Transforms;
|
private int m4Transforms;
|
||||||
private int iMaxZ;
|
private int fMaxZ;
|
||||||
private int iEnableSdf;
|
private int iEnableSdf;
|
||||||
private int iEnableTexture;
|
private int iEnableTexture;
|
||||||
private int iAlphaDiscard;
|
private int iAlphaDiscard;
|
||||||
private int fSdfThreshold;
|
private int fSdfThreshold;
|
||||||
private int txTexture;
|
private int tx2d;
|
||||||
|
private int tx2darray;
|
||||||
|
|
||||||
private bool isDiposed;
|
private bool isDiposed;
|
||||||
private readonly Dictionary<DrawQueue, DrawData> data = new Dictionary<DrawQueue, DrawData>();
|
private readonly Dictionary<DrawQueue, DrawData> data = new Dictionary<DrawQueue, DrawData>();
|
||||||
|
private readonly TextureManager textures = new TextureManager();
|
||||||
|
|
||||||
public bool IsInit { get; private set; } = false;
|
public bool IsInit { get; private set; } = false;
|
||||||
public event Action<GL21Driver> OnGCDispose;
|
public event Action<GL21Driver>? OnGCDispose;
|
||||||
|
|
||||||
public GL21Driver()
|
public GL21Driver()
|
||||||
{
|
{
|
||||||
@@ -38,8 +46,8 @@ namespace Quik.OpenGL
|
|||||||
{
|
{
|
||||||
if (IsInit) return;
|
if (IsInit) return;
|
||||||
|
|
||||||
int vs = CreateShader(GL_VERTEX_SHADER, "Quik.res.gl21.vert");
|
int vs = CreateShader(GL_VERTEX_SHADER, "Dashboard.res.gl21.vert");
|
||||||
int fs = CreateShader(GL_FRAGMENT_SHADER, "Quik.res.gl21.frag");
|
int fs = CreateShader(GL_FRAGMENT_SHADER, "Dashboard.res.gl21.frag");
|
||||||
|
|
||||||
program = GL.CreateProgram();
|
program = GL.CreateProgram();
|
||||||
GL.AttachShader(program, vs);
|
GL.AttachShader(program, vs);
|
||||||
@@ -57,16 +65,19 @@ namespace Quik.OpenGL
|
|||||||
GL.DeleteShader(fs);
|
GL.DeleteShader(fs);
|
||||||
|
|
||||||
v2Position = GL.GetAttribLocation(program, nameof(v2Position));
|
v2Position = GL.GetAttribLocation(program, nameof(v2Position));
|
||||||
iZIndex = GL.GetAttribLocation(program, nameof(iZIndex));
|
fZIndex = GL.GetAttribLocation(program, nameof(fZIndex));
|
||||||
v2TexPos = GL.GetAttribLocation(program, nameof(v2TexPos));
|
v2TexPos = GL.GetAttribLocation(program, nameof(v2TexPos));
|
||||||
|
fTexLayer = GL.GetAttribLocation(program, nameof(fTexLayer));
|
||||||
v4Color = GL.GetAttribLocation(program, nameof(v4Color));
|
v4Color = GL.GetAttribLocation(program, nameof(v4Color));
|
||||||
|
|
||||||
m4Transforms = GL.GetUniformLocation(program, nameof(m4Transforms));
|
m4Transforms = GL.GetUniformLocation(program, nameof(m4Transforms));
|
||||||
iMaxZ = GL.GetUniformLocation(program, nameof(iMaxZ));
|
fMaxZ = GL.GetUniformLocation(program, nameof(fMaxZ));
|
||||||
|
fSdfThreshold = GL.GetUniformLocation(program, nameof(fSdfThreshold));
|
||||||
iEnableSdf = GL.GetUniformLocation(program, nameof(iEnableSdf));
|
iEnableSdf = GL.GetUniformLocation(program, nameof(iEnableSdf));
|
||||||
iEnableTexture = GL.GetUniformLocation(program, nameof(iEnableSdf));
|
iEnableTexture = GL.GetUniformLocation(program, nameof(iEnableTexture));
|
||||||
iAlphaDiscard = GL.GetUniformLocation(program, nameof(iAlphaDiscard));
|
iAlphaDiscard = GL.GetUniformLocation(program, nameof(iAlphaDiscard));
|
||||||
txTexture = GL.GetUniformLocation(program, nameof(txTexture));
|
tx2d = GL.GetUniformLocation(program, nameof(tx2d));
|
||||||
|
tx2darray = GL.GetUniformLocation(program, nameof(tx2darray));
|
||||||
|
|
||||||
IsInit = true;
|
IsInit = true;
|
||||||
}
|
}
|
||||||
@@ -80,7 +91,7 @@ namespace Quik.OpenGL
|
|||||||
{
|
{
|
||||||
AssertInit();
|
AssertInit();
|
||||||
|
|
||||||
if (!data.TryGetValue(queue, out DrawData draw))
|
if (!data.TryGetValue(queue, out DrawData? draw))
|
||||||
{
|
{
|
||||||
draw = new DrawData(this, queue);
|
draw = new DrawData(this, queue);
|
||||||
data.Add(queue, draw);
|
data.Add(queue, draw);
|
||||||
@@ -89,27 +100,51 @@ namespace Quik.OpenGL
|
|||||||
// This already binds the vertex array for me.
|
// This already binds the vertex array for me.
|
||||||
draw.PrepareFrame();
|
draw.PrepareFrame();
|
||||||
|
|
||||||
QVec2 size = view.Size;
|
|
||||||
QMat4.Orthographic(out QMat4 matrix, view);
|
|
||||||
|
|
||||||
|
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.UseProgram(program);
|
||||||
GL.Uniform1(iMaxZ, queue.ZDepth+1);
|
GL.Uniform1(fMaxZ, (float)(queue.ZDepth+1));
|
||||||
GL.UniformMatrix4(m4Transforms, false, in matrix);
|
GL.Uniform1(fSdfThreshold, 0.5f);
|
||||||
GL.Uniform1(fSdfThreshold, 0.0f);
|
GL.Uniform1(tx2d, 0);
|
||||||
GL.Uniform1(txTexture, 0);
|
|
||||||
GL.Enable(GL_BLEND);
|
GL.Enable(GL_BLEND);
|
||||||
GL.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
GL.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
GL.Enable(GL_SCISSOR_TEST);
|
||||||
|
GL.Enable(GL_DEPTH_TEST);
|
||||||
|
GL.DepthFunc(GL_LESS);
|
||||||
|
|
||||||
foreach (DrawCall call in queue)
|
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.ActiveTexture(GL_TEXTURE0);
|
||||||
GL.BindTexture(GL_TEXTURE_2D, 0);
|
GL.BindTexture(GL_TEXTURE_2D, 0);
|
||||||
if (call.Texture != null)
|
if (call.Texture != null)
|
||||||
{
|
{
|
||||||
// TODO: actually use textures?
|
GL.Uniform1(iEnableSdf, call.Texture.IsSdf ? 1 : 0);
|
||||||
GL.Uniform1(iEnableTexture, 1);
|
|
||||||
GL.Uniform1(iEnableSdf, call.Texture.SignedDistanceField ? 1 : 0);
|
|
||||||
GL.Uniform1(iAlphaDiscard, 1);
|
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
|
else
|
||||||
{
|
{
|
||||||
@@ -118,13 +153,17 @@ namespace Quik.OpenGL
|
|||||||
|
|
||||||
GL.DrawElements(GL_TRIANGLES, call.Count, GL_UNSIGNED_INT, sizeof(int)*call.Start);
|
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)
|
public void ClearDrawQueue(DrawQueue queue)
|
||||||
{
|
{
|
||||||
AssertInit();
|
AssertInit();
|
||||||
|
|
||||||
if (!data.TryGetValue(queue, out DrawData draw))
|
if (!data.TryGetValue(queue, out DrawData? draw))
|
||||||
return;
|
return;
|
||||||
draw.Dispose();
|
draw.Dispose();
|
||||||
data.Remove(queue);
|
data.Remove(queue);
|
||||||
@@ -132,7 +171,7 @@ namespace Quik.OpenGL
|
|||||||
|
|
||||||
private static int CreateShader(GLEnum type, string name)
|
private static int CreateShader(GLEnum type, string name)
|
||||||
{
|
{
|
||||||
StreamReader source = new StreamReader(typeof(GL21Driver).Assembly.GetManifestResourceStream(name));
|
StreamReader source = new StreamReader(typeof(GL21Driver).Assembly.GetManifestResourceStream(name) ?? throw new Exception("Resource not found."));
|
||||||
string text = source.ReadToEnd();
|
string text = source.ReadToEnd();
|
||||||
source.Dispose();
|
source.Dispose();
|
||||||
|
|
||||||
@@ -254,13 +293,15 @@ namespace Quik.OpenGL
|
|||||||
GL.BufferData(GL_ARRAY_BUFFER, QuikVertex.Stride * Queue.VertexCount, Queue.VertexArray, GL_STREAM_DRAW);
|
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.v2Position, 2, GL_FLOAT, false, QuikVertex.Stride, QuikVertex.PositionOffset);
|
||||||
GL.VertexAttribIPointer(driver.iZIndex, 1, GL_UNSIGNED_INT, QuikVertex.Stride, QuikVertex.ZIndexOffset);
|
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.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.VertexAttribPointer(driver.v4Color, 4, GL_UNSIGNED_BYTE, true, QuikVertex.Stride, QuikVertex.ColorOffset);
|
||||||
GL.EnableVertexAttribArray(driver.v2Position);
|
GL.EnableVertexAttribArray(driver.v2Position);
|
||||||
GL.EnableVertexAttribArray(driver.iZIndex);
|
GL.EnableVertexAttribArray(driver.fZIndex);
|
||||||
GL.EnableVertexAttribArray(driver.v2TexPos);
|
GL.EnableVertexAttribArray(driver.v2TexPos);
|
||||||
GL.EnableVertexAttribArray(driver.v4Color);
|
GL.EnableVertexAttribArray(driver.v4Color);
|
||||||
|
GL.EnableVertexAttribArray(driver.fTexLayer);
|
||||||
|
|
||||||
GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||||
GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, Queue.ElementCount * sizeof(int), Queue.ElementArray, GL_STREAM_DRAW);
|
GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, Queue.ElementCount * sizeof(int), Queue.ElementArray, GL_STREAM_DRAW);
|
||||||
@@ -287,4 +328,133 @@ namespace Quik.OpenGL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,27 @@
|
|||||||
namespace Quik.OpenGL
|
namespace Dashboard.OpenGL
|
||||||
{
|
{
|
||||||
public enum GLEnum : int
|
public enum GLEnum : int
|
||||||
{
|
{
|
||||||
GL_OK = 0,
|
GL_OK = 0,
|
||||||
GL_TRUE = 1,
|
GL_TRUE = 1,
|
||||||
GL_FALSE = 0,
|
GL_FALSE = 0,
|
||||||
|
GL_ONE = 1,
|
||||||
|
GL_ZERO = 0,
|
||||||
GL_MAJOR_VERSION = 0x821B,
|
GL_MAJOR_VERSION = 0x821B,
|
||||||
GL_MINOR_VERSION = 0x821C,
|
GL_MINOR_VERSION = 0x821C,
|
||||||
GL_VENDOR = 0x1F00,
|
GL_VENDOR = 0x1F00,
|
||||||
GL_RENDERER = 0x1F01,
|
GL_RENDERER = 0x1F01,
|
||||||
GL_VERSION = 0x1F02,
|
GL_VERSION = 0x1F02,
|
||||||
GL_EXTENSIONS = 0x1F03,
|
GL_EXTENSIONS = 0x1F03,
|
||||||
|
GL_MAX_TEXTURE_SIZE = 0x0D33,
|
||||||
|
GL_MAX_3D_TEXTURE_SIZE = 0x8073,
|
||||||
|
GL_MAX_ARRAY_TEXTURE_LAYERS = 0x88FF,
|
||||||
|
|
||||||
GL_MULTISAMPLE = 0x809D,
|
GL_MULTISAMPLE = 0x809D,
|
||||||
GL_BLEND = 0x0BE2,
|
GL_BLEND = 0x0BE2,
|
||||||
|
|
||||||
GL_COLOR_BUFFER_BIT = 0x00004000,
|
GL_COLOR_BUFFER_BIT = 0x00004000,
|
||||||
|
GL_DEPTH_BUFFER_BIT = 0x00000100,
|
||||||
|
|
||||||
GL_SRC_ALPHA = 0x0302,
|
GL_SRC_ALPHA = 0x0302,
|
||||||
GL_ONE_MINUS_SRC_ALPHA = 0x0303,
|
GL_ONE_MINUS_SRC_ALPHA = 0x0303,
|
||||||
@@ -32,6 +38,8 @@ namespace Quik.OpenGL
|
|||||||
GL_FLOAT = 0x1406,
|
GL_FLOAT = 0x1406,
|
||||||
|
|
||||||
GL_RED = 0x1903,
|
GL_RED = 0x1903,
|
||||||
|
GL_GREEN = 0x1904,
|
||||||
|
GL_BLUE = 0x1905,
|
||||||
GL_ALPHA = 0x1906,
|
GL_ALPHA = 0x1906,
|
||||||
GL_RGB = 0x1907,
|
GL_RGB = 0x1907,
|
||||||
GL_RGBA = 0x1908,
|
GL_RGBA = 0x1908,
|
||||||
@@ -50,6 +58,7 @@ namespace Quik.OpenGL
|
|||||||
GL_TEXTURE6 = GL_TEXTURE0 + 6,
|
GL_TEXTURE6 = GL_TEXTURE0 + 6,
|
||||||
|
|
||||||
GL_TEXTURE_2D = 0x0DE1,
|
GL_TEXTURE_2D = 0x0DE1,
|
||||||
|
GL_TEXTURE_2D_ARRAY = 0x8C1A,
|
||||||
GL_UNPACK_ALIGNMENT = 0x0CF5,
|
GL_UNPACK_ALIGNMENT = 0x0CF5,
|
||||||
|
|
||||||
GL_TEXTURE_MAG_FILTER = 0x2800,
|
GL_TEXTURE_MAG_FILTER = 0x2800,
|
||||||
@@ -71,5 +80,15 @@ namespace Quik.OpenGL
|
|||||||
GL_REPEAT = 0x2901,
|
GL_REPEAT = 0x2901,
|
||||||
|
|
||||||
GL_TRIANGLES = 0x0004,
|
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using static Quik.OpenGL.GLEnum;
|
using static Dashboard.OpenGL.GLEnum;
|
||||||
|
|
||||||
namespace Quik.OpenGL
|
namespace Dashboard.OpenGL
|
||||||
{
|
{
|
||||||
[System.Serializable]
|
[System.Serializable]
|
||||||
public class GraphicsException : System.Exception
|
public class GraphicsException : System.Exception
|
||||||
@@ -1,20 +1,20 @@
|
|||||||
using Quik.CommandMachine;
|
using Dashboard.CommandMachine;
|
||||||
using Quik.Controls;
|
using Dashboard.Controls;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Quik.PAL
|
namespace Dashboard.PAL
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An abstraction layer over the UI input and output.
|
/// An abstraction layer over the UI input and output.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class QuikPort
|
public class Dash
|
||||||
{
|
{
|
||||||
private readonly IQuikPortHandle handle;
|
private readonly IDashHandle handle;
|
||||||
private readonly IQuikPlatform platform;
|
private readonly IDashboardPlatform platform;
|
||||||
|
|
||||||
public string Title
|
public string Title
|
||||||
{
|
{
|
||||||
@@ -34,7 +34,7 @@ namespace Quik.PAL
|
|||||||
set => platform.PortSetPosition(handle, value);
|
set => platform.PortSetPosition(handle, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UIBase UIElement { get; set; }
|
public UIBase? UIElement { get; set; }
|
||||||
|
|
||||||
public bool IsValid => platform.PortIsValid(handle);
|
public bool IsValid => platform.PortIsValid(handle);
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ namespace Quik.PAL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public QuikPort(IQuikPlatform platform)
|
public Dash(IDashboardPlatform platform)
|
||||||
{
|
{
|
||||||
this.platform = platform;
|
this.platform = platform;
|
||||||
handle = platform.CreatePort();
|
handle = platform.CreatePort();
|
||||||
@@ -70,13 +70,12 @@ namespace Quik.PAL
|
|||||||
platform.PortFocus(handle);
|
platform.PortFocus(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Paint(CommandList list = null)
|
public void Paint(CommandList? list = null)
|
||||||
{
|
{
|
||||||
if (UIElement == null)
|
if (UIElement == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(list == null)
|
list ??= new CommandList();
|
||||||
list = new CommandList();
|
|
||||||
|
|
||||||
list.Clear();
|
list.Clear();
|
||||||
UIElement.Bounds = new QRectangle(Size, new QVec2(0,0));
|
UIElement.Bounds = new QRectangle(Size, new QVec2(0,0));
|
||||||
61
Dashboard/PAL/IDashboardPlatform.cs
Normal file
61
Dashboard/PAL/IDashboardPlatform.cs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
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
Dashboard/PAL/IFontDatabase.cs
Normal file
67
Dashboard/PAL/IFontDatabase.cs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
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
Dashboard/PAL/IFontFactory.cs
Normal file
12
Dashboard/PAL/IFontFactory.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,27 +1,28 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Quik.CommandMachine;
|
using System.Threading;
|
||||||
using Quik.Controls;
|
using Dashboard.CommandMachine;
|
||||||
using Quik.Media;
|
using Dashboard.Controls;
|
||||||
using Quik.PAL;
|
using Dashboard.Media;
|
||||||
using Quik.Typography;
|
using Dashboard.PAL;
|
||||||
|
using Dashboard.Typography;
|
||||||
|
|
||||||
namespace Quik
|
namespace Dashboard
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Main class for Quik applications.
|
/// Main class for Quik applications.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class QuikApplication
|
public class DashboardApplication
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The application platform driver.
|
/// The application platform driver.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IQuikPlatform Platform { get; }
|
public IDashboardPlatform Platform { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Title of the application.
|
/// Title of the application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Title
|
public string? Title
|
||||||
{
|
{
|
||||||
get => Platform.Title;
|
get => Platform.Title;
|
||||||
set => Platform.Title = value;
|
set => Platform.Title = value;
|
||||||
@@ -30,13 +31,13 @@ namespace Quik
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Application icon.
|
/// Application icon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public QImage Icon
|
public QImage? Icon
|
||||||
{
|
{
|
||||||
get => Platform.Icon;
|
get => Platform.Icon;
|
||||||
set => Platform.Icon = value;
|
set => Platform.Icon = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QuikPort MainPort { get; private set; } = null;
|
public PAL.Dash? MainPort { get; private set; } = null;
|
||||||
|
|
||||||
public FontProvider FontProvider { get; }
|
public FontProvider FontProvider { get; }
|
||||||
|
|
||||||
@@ -45,15 +46,16 @@ namespace Quik
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public List<MediaLoader> MediaLoaders { get; } = new List<MediaLoader>();
|
public List<MediaLoader> MediaLoaders { get; } = new List<MediaLoader>();
|
||||||
|
|
||||||
public QuikApplication(IQuikPlatform platform)
|
public DashboardApplication(IDashboardPlatform platform)
|
||||||
{
|
{
|
||||||
Platform = platform;
|
Platform = platform;
|
||||||
FontProvider = new FontProvider(this);
|
FontProvider = new FontProvider(this);
|
||||||
|
Current = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDisposable GetMedia(object key, MediaHint hint)
|
public IDisposable? GetMedia(object key, MediaHint hint)
|
||||||
{
|
{
|
||||||
IDisposable disposable = null;
|
IDisposable? disposable = null;
|
||||||
|
|
||||||
foreach (MediaLoader loader in MediaLoaders)
|
foreach (MediaLoader loader in MediaLoaders)
|
||||||
{
|
{
|
||||||
@@ -66,9 +68,9 @@ namespace Quik
|
|||||||
return disposable;
|
return disposable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDisposable GetMedia<T>(T key, MediaHint hint)
|
public IDisposable? GetMedia<T>(T key, MediaHint hint)
|
||||||
{
|
{
|
||||||
IDisposable disposable = null;
|
IDisposable? disposable = null;
|
||||||
|
|
||||||
foreach (MediaLoader loader in MediaLoaders)
|
foreach (MediaLoader loader in MediaLoaders)
|
||||||
{
|
{
|
||||||
@@ -84,22 +86,48 @@ namespace Quik
|
|||||||
return disposable;
|
return disposable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run(View mainView)
|
private CommandList cmd = new CommandList();
|
||||||
|
|
||||||
|
public void Run(View mainView, bool yield = true)
|
||||||
{
|
{
|
||||||
MainPort = new QuikPort(Platform) { UIElement = mainView };
|
Init(mainView);
|
||||||
CommandList cmd = new CommandList();
|
|
||||||
|
|
||||||
MainPort.EventRaised += (sender, ea) => mainView.NotifyEvent(sender, ea);
|
while (RunSync())
|
||||||
|
|
||||||
while (MainPort.IsValid)
|
|
||||||
{
|
{
|
||||||
Platform.ProcessEvents(false);
|
if (yield)
|
||||||
if (MainPort.IsValid)
|
|
||||||
{
|
{
|
||||||
cmd.Clear();
|
Thread.Yield();
|
||||||
MainPort.Paint(cmd);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ using System;
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Quik
|
namespace Dashboard
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A 2 dimensional Vector.
|
/// A 2 dimensional Vector.
|
||||||
@@ -66,7 +66,7 @@ namespace Quik
|
|||||||
|
|
||||||
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)
|
public override bool Equals(object? obj)
|
||||||
{
|
{
|
||||||
if (obj is QVec2)
|
if (obj is QVec2)
|
||||||
{
|
{
|
||||||
@@ -92,6 +92,10 @@ namespace Quik
|
|||||||
{
|
{
|
||||||
return $"({X}; {Y})";
|
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>
|
/// <summary>
|
||||||
@@ -481,9 +485,9 @@ namespace Quik
|
|||||||
public static void Translation(out QMat4 mat, float x, float y, float z)
|
public static void Translation(out QMat4 mat, float x, float y, float z)
|
||||||
{
|
{
|
||||||
mat = Identity;
|
mat = Identity;
|
||||||
mat.M41 = x;
|
mat.M14 = x;
|
||||||
mat.M42 = y;
|
mat.M24 = y;
|
||||||
mat.M43 = z;
|
mat.M34 = z;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Scale(out QMat4 mat, float x, float y, float z)
|
public static void Scale(out QMat4 mat, float x, float y, float z)
|
||||||
@@ -513,5 +517,32 @@ namespace Quik
|
|||||||
mat.M34 = -c * (far + near);
|
mat.M34 = -c * (far + near);
|
||||||
mat.M44 = 1.0f;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Quik.Media;
|
using Dashboard.Media;
|
||||||
using Quik.Typography;
|
using Dashboard.Media.Font;
|
||||||
|
|
||||||
namespace Quik
|
namespace Dashboard
|
||||||
{
|
{
|
||||||
public enum TextAlignment
|
public enum TextAlignment
|
||||||
{
|
{
|
||||||
@@ -53,7 +53,7 @@ namespace Quik
|
|||||||
|
|
||||||
public abstract class StyleBase
|
public abstract class StyleBase
|
||||||
{
|
{
|
||||||
public abstract object this[string key] { get; set; }
|
public abstract object? this[string key] { get; set; }
|
||||||
|
|
||||||
public QColor? Color
|
public QColor? Color
|
||||||
{
|
{
|
||||||
@@ -109,9 +109,9 @@ namespace Quik
|
|||||||
set => this["list-marker-position"] = value;
|
set => this["list-marker-position"] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QuikTexture ListMarkerImage
|
public QImage? ListMarkerImage
|
||||||
{
|
{
|
||||||
get => (QuikTexture)this["list-marker-image"];
|
get => (QImage?)this["list-marker-image"];
|
||||||
set => this["list-marker-image"] = value;
|
set => this["list-marker-image"] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,9 +127,9 @@ namespace Quik
|
|||||||
set => this["stroke-color"] = value;
|
set => this["stroke-color"] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FontInfo Font
|
public FontFace? Font
|
||||||
{
|
{
|
||||||
get => (FontInfo)this["font"];
|
get => (FontFace?)this["font"];
|
||||||
set => this["font"] = value;
|
set => this["font"] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,9 +144,9 @@ namespace Quik
|
|||||||
{
|
{
|
||||||
private readonly Dictionary<string, object> _keys = new Dictionary<string, object>();
|
private readonly Dictionary<string, object> _keys = new Dictionary<string, object>();
|
||||||
|
|
||||||
public override object this[string styleKey]
|
public override object? this[string styleKey]
|
||||||
{
|
{
|
||||||
get => _keys.TryGetValue(styleKey, out object value) ? value : null;
|
get => _keys.TryGetValue(styleKey, out object? value) ? value : null;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null)
|
||||||
@@ -163,11 +163,11 @@ namespace Quik
|
|||||||
|
|
||||||
public Style BaseStyle { get; }
|
public Style BaseStyle { get; }
|
||||||
|
|
||||||
public override object this[string key]
|
public override object? this[string key]
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
object value = null;
|
object? value = null;
|
||||||
|
|
||||||
for (int i = _styles.Count; i != 0 && value == null; i--)
|
for (int i = _styles.Count; i != 0 && value == null; i--)
|
||||||
{
|
{
|
||||||
108
Dashboard/Typography/FontProvider.cs
Normal file
108
Dashboard/Typography/FontProvider.cs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,9 +3,9 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Quik.Media;
|
using Dashboard.Media;
|
||||||
|
|
||||||
namespace Quik.Typography
|
namespace Dashboard.Typography
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An atomic horizontal block of text which cannot be further divided.
|
/// An atomic horizontal block of text which cannot be further divided.
|
||||||
@@ -226,7 +226,7 @@ namespace Quik.Typography
|
|||||||
|
|
||||||
pen.Y -= PostSpace;
|
pen.Y -= PostSpace;
|
||||||
|
|
||||||
group.BoundingBox = new QRectangle(width, 0, 0, pen.Y);
|
group.BoundingBox = new QRectangle(width, pen.Y, 0, 0);
|
||||||
group.Translate(-pen);
|
group.Translate(-pen);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,13 +353,13 @@ namespace Quik.Typography
|
|||||||
public struct TypesetCharacter
|
public struct TypesetCharacter
|
||||||
{
|
{
|
||||||
public int Character;
|
public int Character;
|
||||||
public QuikTexture Texture;
|
public QImage Texture;
|
||||||
public QRectangle Position;
|
public QRectangle Position;
|
||||||
public QRectangle UV;
|
public QRectangle UV;
|
||||||
|
|
||||||
public TypesetCharacter(
|
public TypesetCharacter(
|
||||||
int chr,
|
int chr,
|
||||||
QuikTexture texture,
|
QImage texture,
|
||||||
in QRectangle position,
|
in QRectangle position,
|
||||||
in QRectangle uv)
|
in QRectangle uv)
|
||||||
{
|
{
|
||||||
181
Dashboard/Typography/Typesetter.cs
Normal file
181
Dashboard/Typography/Typesetter.cs
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Quik.Typography
|
namespace Dashboard.Typography
|
||||||
{
|
{
|
||||||
public static class UnicodeUtil
|
public static class UnicodeUtil
|
||||||
{
|
{
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
|
using Dashboard.Media;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Quik.VertexGenerator
|
namespace Dashboard.VertexGenerator
|
||||||
{
|
{
|
||||||
public class DrawQueue : IEnumerable<DrawCall>
|
public class DrawQueue : IEnumerable<DrawCall>
|
||||||
{
|
{
|
||||||
@@ -13,7 +14,7 @@ namespace Quik.VertexGenerator
|
|||||||
private int _start;
|
private int _start;
|
||||||
private int _baseOffset;
|
private int _baseOffset;
|
||||||
private QRectangle _bounds;
|
private QRectangle _bounds;
|
||||||
private QuikTexture _texture;
|
private QImage? _texture;
|
||||||
|
|
||||||
public int ZDepth { get; private set; }
|
public int ZDepth { get; private set; }
|
||||||
public QuikVertex[] VertexArray => _vertices.InternalArray;
|
public QuikVertex[] VertexArray => _vertices.InternalArray;
|
||||||
@@ -32,7 +33,7 @@ namespace Quik.VertexGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void StartDrawCall(in QRectangle bounds, QuikTexture texture, int baseOffset)
|
public void StartDrawCall(in QRectangle bounds, QImage? texture, int baseOffset)
|
||||||
{
|
{
|
||||||
_start = ElementCount;
|
_start = ElementCount;
|
||||||
_texture = texture;
|
_texture = texture;
|
||||||
@@ -47,7 +48,7 @@ namespace Quik.VertexGenerator
|
|||||||
public void StartDrawCall(in QRectangle bounds, int baseOffset) => StartDrawCall(bounds, null, baseOffset);
|
public void StartDrawCall(in QRectangle bounds, int baseOffset) => StartDrawCall(bounds, null, baseOffset);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void StartDrawCall(in QRectangle bounds, QuikTexture texture) => StartDrawCall(bounds, texture, _vertices.Count);
|
public void StartDrawCall(in QRectangle bounds, QImage texture) => StartDrawCall(bounds, texture, _vertices.Count);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void AddVertex(in QuikVertex vertex)
|
public void AddVertex(in QuikVertex vertex)
|
||||||
@@ -99,9 +100,9 @@ namespace Quik.VertexGenerator
|
|||||||
public int Start { get; }
|
public int Start { get; }
|
||||||
public int Count { get; }
|
public int Count { get; }
|
||||||
public QRectangle Bounds { get; }
|
public QRectangle Bounds { get; }
|
||||||
public QuikTexture Texture { get; }
|
public QImage? Texture { get; }
|
||||||
|
|
||||||
public DrawCall(int start, int count, in QRectangle bounds, QuikTexture texture)
|
public DrawCall(int start, int count, in QRectangle bounds, QImage? texture)
|
||||||
{
|
{
|
||||||
Start = start;
|
Start = start;
|
||||||
Count = count;
|
Count = count;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Quik.VertexGenerator
|
namespace Dashboard.VertexGenerator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a GPU vertex.
|
/// Represents a GPU vertex.
|
||||||
@@ -28,10 +28,16 @@ namespace Quik.VertexGenerator
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int ZIndex;
|
public int ZIndex;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The texture layer to draw for 3d images.
|
||||||
|
/// </summary>
|
||||||
|
public float TextureLayer;
|
||||||
|
|
||||||
public static int PositionOffset => 0;
|
public static int PositionOffset => 0;
|
||||||
public static unsafe int TextureCoordinatesOffset => sizeof(QVec2);
|
public static unsafe int TextureCoordinatesOffset => sizeof(QVec2);
|
||||||
public static unsafe int ColorOffset => 2 * sizeof(QVec2);
|
public static unsafe int ColorOffset => 2 * sizeof(QVec2);
|
||||||
public static unsafe int ZIndexOffset => ColorOffset + sizeof(QColor);
|
public static unsafe int ZIndexOffset => ColorOffset + sizeof(QColor);
|
||||||
|
public static unsafe int TextureLayerOffset => ZIndexOffset + sizeof(int);
|
||||||
public static unsafe int Stride => sizeof(QuikVertex);
|
public static unsafe int Stride => sizeof(QuikVertex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Quik.VertexGenerator
|
namespace Dashboard.VertexGenerator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A small list which whose items can be used by reference.
|
/// A small list which whose items can be used by reference.
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Quik.CommandMachine;
|
using Dashboard.CommandMachine;
|
||||||
|
using Dashboard.Media;
|
||||||
|
using Dashboard.Typography;
|
||||||
|
|
||||||
namespace Quik.VertexGenerator
|
namespace Dashboard.VertexGenerator
|
||||||
{
|
{
|
||||||
public class VertexGeneratorEngine : CommandEngine
|
public class VertexGeneratorEngine : CommandEngine
|
||||||
{
|
{
|
||||||
@@ -13,6 +15,9 @@ namespace Quik.VertexGenerator
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected float CurveGranularity =>
|
protected float CurveGranularity =>
|
||||||
(Style["-vertex-curve-granularity"] is float value) ? value : 1.0f;
|
(Style["-vertex-curve-granularity"] is float value) ? value : 1.0f;
|
||||||
|
protected bool BlendTextures =>
|
||||||
|
(Style["-vertex-blend-textures"] is bool value) ? value : false;
|
||||||
|
|
||||||
protected QuikVertex StrokeVertex => new QuikVertex()
|
protected QuikVertex StrokeVertex => new QuikVertex()
|
||||||
{
|
{
|
||||||
ZIndex = Style.ZIndex ?? this.ZIndex,
|
ZIndex = Style.ZIndex ?? this.ZIndex,
|
||||||
@@ -24,13 +29,19 @@ namespace Quik.VertexGenerator
|
|||||||
Color = Style.Color ?? QColor.White,
|
Color = Style.Color ?? QColor.White,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected QuikVertex ImageVertex => new QuikVertex()
|
||||||
|
{
|
||||||
|
ZIndex = Style.ZIndex ?? this.ZIndex,
|
||||||
|
Color = BlendTextures ? (Style.Color ?? QColor.White) : QColor.White,
|
||||||
|
};
|
||||||
|
|
||||||
public override void Reset()
|
public override void Reset()
|
||||||
{
|
{
|
||||||
base.Reset();
|
base.Reset();
|
||||||
DrawQueue.Clear();
|
DrawQueue.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ChildProcessCommand(Command name, CommandEnumerator queue)
|
protected override void ChildProcessCommand(Command name, CommandQueue queue)
|
||||||
{
|
{
|
||||||
base.ChildProcessCommand(name, queue);
|
base.ChildProcessCommand(name, queue);
|
||||||
|
|
||||||
@@ -39,6 +50,7 @@ namespace Quik.VertexGenerator
|
|||||||
case Command.Line: LineProc(queue); break;
|
case Command.Line: LineProc(queue); break;
|
||||||
case Command.Bezier: BezierProc(queue); break;
|
case Command.Bezier: BezierProc(queue); break;
|
||||||
case Command.Rectangle: RectangleProc(queue); break;
|
case Command.Rectangle: RectangleProc(queue); break;
|
||||||
|
case Command.Image: ImageProc(queue); break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,7 +67,7 @@ namespace Quik.VertexGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<QLine> LineList = new List<QLine>();
|
private readonly List<QLine> LineList = new List<QLine>();
|
||||||
private void LineProc(CommandEnumerator queue)
|
private void LineProc(CommandQueue queue)
|
||||||
{
|
{
|
||||||
Frame frame = queue.Dequeue();
|
Frame frame = queue.Dequeue();
|
||||||
|
|
||||||
@@ -280,7 +292,7 @@ namespace Quik.VertexGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<QBezier> BezierList = new List<QBezier>();
|
private readonly List<QBezier> BezierList = new List<QBezier>();
|
||||||
private void BezierProc(CommandEnumerator queue)
|
private void BezierProc(CommandQueue queue)
|
||||||
{
|
{
|
||||||
Frame a = queue.Dequeue();
|
Frame a = queue.Dequeue();
|
||||||
Frame b;
|
Frame b;
|
||||||
@@ -397,7 +409,7 @@ namespace Quik.VertexGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<QRectangle> RectangleList = new List<QRectangle>();
|
private readonly List<QRectangle> RectangleList = new List<QRectangle>();
|
||||||
private void RectangleProc(CommandEnumerator queue)
|
private void RectangleProc(CommandQueue queue)
|
||||||
{
|
{
|
||||||
Frame frame = queue.Dequeue();
|
Frame frame = queue.Dequeue();
|
||||||
RectangleList.Clear();
|
RectangleList.Clear();
|
||||||
@@ -422,8 +434,8 @@ namespace Quik.VertexGenerator
|
|||||||
{
|
{
|
||||||
QRectangle outer = RectangleList[i];
|
QRectangle outer = RectangleList[i];
|
||||||
QRectangle inner = new QRectangle(
|
QRectangle inner = new QRectangle(
|
||||||
outer.Right - stroke, outer.Bottom + stroke,
|
outer.Right - stroke, outer.Bottom - stroke,
|
||||||
outer.Left + stroke, outer.Top - stroke);
|
outer.Left + stroke, outer.Top + stroke);
|
||||||
|
|
||||||
GenerateRectangleBase(inner, Math.Max(radius - stroke, 0.0f));
|
GenerateRectangleBase(inner, Math.Max(radius - stroke, 0.0f));
|
||||||
|
|
||||||
@@ -469,13 +481,13 @@ namespace Quik.VertexGenerator
|
|||||||
QVec2 aPos, bPos, cPos, dPos;
|
QVec2 aPos, bPos, cPos, dPos;
|
||||||
|
|
||||||
QuikVertex v = FillVertex;
|
QuikVertex v = FillVertex;
|
||||||
aPos = v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom + radius);
|
aPos = v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom - radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
bPos = v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom + radius);
|
bPos = v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom - radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
cPos = v.Position = new QVec2(rectangle.Right - radius, rectangle.Top - radius);
|
cPos = v.Position = new QVec2(rectangle.Right - radius, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
dPos = v.Position = new QVec2(rectangle.Left + radius, rectangle.Top - radius);
|
dPos = v.Position = new QVec2(rectangle.Left + radius, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
DrawQueue.AddElement(0); DrawQueue.AddElement(1); DrawQueue.AddElement(2);
|
DrawQueue.AddElement(0); DrawQueue.AddElement(1); DrawQueue.AddElement(2);
|
||||||
@@ -496,9 +508,9 @@ namespace Quik.VertexGenerator
|
|||||||
|
|
||||||
// Draw east rectangle.
|
// Draw east rectangle.
|
||||||
|
|
||||||
v.Position = new QVec2(rectangle.Right, rectangle.Bottom + radius);
|
v.Position = new QVec2(rectangle.Right, rectangle.Bottom - radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right, rectangle.Top - radius);
|
v.Position = new QVec2(rectangle.Right, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
DrawQueue.AddElement(1); DrawQueue.AddElement(6); DrawQueue.AddElement(7);
|
DrawQueue.AddElement(1); DrawQueue.AddElement(6); DrawQueue.AddElement(7);
|
||||||
@@ -516,9 +528,9 @@ namespace Quik.VertexGenerator
|
|||||||
|
|
||||||
// Draw west rectangle.
|
// Draw west rectangle.
|
||||||
|
|
||||||
v.Position = new QVec2(rectangle.Left, rectangle.Top - radius);
|
v.Position = new QVec2(rectangle.Left, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left, rectangle.Bottom + radius);
|
v.Position = new QVec2(rectangle.Left, rectangle.Bottom - radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
DrawQueue.AddElement(11); DrawQueue.AddElement(0); DrawQueue.AddElement(3);
|
DrawQueue.AddElement(11); DrawQueue.AddElement(0); DrawQueue.AddElement(3);
|
||||||
@@ -609,13 +621,13 @@ namespace Quik.VertexGenerator
|
|||||||
|
|
||||||
DrawQueue.RestoreOffset();
|
DrawQueue.RestoreOffset();
|
||||||
|
|
||||||
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Bottom + stroke);
|
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Bottom - stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Bottom + stroke);
|
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Bottom - stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Top - stroke);
|
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Top + stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Top - stroke);
|
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Top + stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
v.Position = new QVec2(rectangle.Left, rectangle.Bottom);
|
v.Position = new QVec2(rectangle.Left, rectangle.Bottom);
|
||||||
@@ -669,13 +681,13 @@ namespace Quik.VertexGenerator
|
|||||||
|
|
||||||
// a-b-c-d
|
// a-b-c-d
|
||||||
|
|
||||||
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Bottom + stroke);
|
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Bottom - stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Bottom + stroke);
|
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Bottom - stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Top - stroke);
|
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Top + stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Top - stroke);
|
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Top + stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
// ef-gh-ij-kl
|
// ef-gh-ij-kl
|
||||||
@@ -684,24 +696,24 @@ namespace Quik.VertexGenerator
|
|||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Bottom);
|
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Bottom);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right, rectangle.Bottom + stroke);
|
v.Position = new QVec2(rectangle.Right, rectangle.Bottom - stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right, rectangle.Top - stroke);
|
v.Position = new QVec2(rectangle.Right, rectangle.Top + stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Top);
|
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Top);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Top);
|
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Top);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left, rectangle.Top - stroke);
|
v.Position = new QVec2(rectangle.Left, rectangle.Top + stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left, rectangle.Bottom + stroke);
|
v.Position = new QVec2(rectangle.Left, rectangle.Bottom - stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
// mno
|
// mno
|
||||||
|
|
||||||
v.Position = new QVec2(rectangle.Left, rectangle.Bottom + radius);
|
v.Position = new QVec2(rectangle.Left, rectangle.Bottom - radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
nPos = v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom + radius);
|
nPos = v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom - radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom);
|
v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
@@ -709,15 +721,15 @@ namespace Quik.VertexGenerator
|
|||||||
// pqr
|
// pqr
|
||||||
v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom);
|
v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
qPos = v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom + radius);
|
qPos = v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom - radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right, rectangle.Bottom + radius);
|
v.Position = new QVec2(rectangle.Right, rectangle.Bottom - radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
// stu
|
// stu
|
||||||
v.Position = new QVec2(rectangle.Right, rectangle.Top - radius);
|
v.Position = new QVec2(rectangle.Right, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
tPos = v.Position = new QVec2(rectangle.Right - radius, rectangle.Top - radius);
|
tPos = v.Position = new QVec2(rectangle.Right - radius, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - radius, rectangle.Top);
|
v.Position = new QVec2(rectangle.Right - radius, rectangle.Top);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
@@ -726,9 +738,9 @@ namespace Quik.VertexGenerator
|
|||||||
|
|
||||||
v.Position = new QVec2(rectangle.Left + radius, rectangle.Top);
|
v.Position = new QVec2(rectangle.Left + radius, rectangle.Top);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
wPos = v.Position = new QVec2(rectangle.Left + radius, rectangle.Top - radius);
|
wPos = v.Position = new QVec2(rectangle.Left + radius, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left, rectangle.Top - radius);
|
v.Position = new QVec2(rectangle.Left, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
// E
|
// E
|
||||||
@@ -859,23 +871,23 @@ namespace Quik.VertexGenerator
|
|||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom);
|
v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom + stroke);
|
v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom - stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom + stroke);
|
v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom - stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Bottom + radius);
|
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Bottom - radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right, rectangle.Top - radius);
|
v.Position = new QVec2(rectangle.Right, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right, rectangle.Top - radius);
|
v.Position = new QVec2(rectangle.Right, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Bottom + radius);
|
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Bottom - radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
v.Position = new QVec2(rectangle.Left + radius, rectangle.Top - stroke);
|
v.Position = new QVec2(rectangle.Left + radius, rectangle.Top + stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - radius, rectangle.Top - stroke);
|
v.Position = new QVec2(rectangle.Right - radius, rectangle.Top + stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - radius, rectangle.Top);
|
v.Position = new QVec2(rectangle.Right - radius, rectangle.Top);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
@@ -884,9 +896,9 @@ namespace Quik.VertexGenerator
|
|||||||
|
|
||||||
v.Position = new QVec2(rectangle.Left, rectangle.Bottom + radius);
|
v.Position = new QVec2(rectangle.Left, rectangle.Bottom + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Top - radius);
|
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Top - radius);
|
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left, rectangle.Bottom + radius);
|
v.Position = new QVec2(rectangle.Left, rectangle.Bottom + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
@@ -1000,6 +1012,104 @@ namespace Quik.VertexGenerator
|
|||||||
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(4);
|
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ImageProc(CommandQueue queue)
|
||||||
|
{
|
||||||
|
Frame frame = queue.Dequeue();
|
||||||
|
ImageCommandFlags flags = (ImageCommandFlags)frame.I1;
|
||||||
|
QImage image = queue.Dequeue().As<QImage>();
|
||||||
|
|
||||||
|
int count = flags.HasFlag(ImageCommandFlags.Single) ? 1 : frame.I2;
|
||||||
|
if (flags.HasFlag(ImageCommandFlags.Image3d))
|
||||||
|
{
|
||||||
|
Image3d(queue, image, count);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Image2d(queue, image, count, flags.HasFlag(ImageCommandFlags.UVs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Image2d(CommandQueue queue, QImage image, int count, bool uv)
|
||||||
|
{
|
||||||
|
DrawQueue.StartDrawCall(Viewport, image);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
QRectangle rect = (QRectangle)queue.Dequeue();
|
||||||
|
QRectangle uvs;
|
||||||
|
|
||||||
|
if (uv)
|
||||||
|
{
|
||||||
|
uvs = (QRectangle)queue.Dequeue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uvs = new QRectangle(1, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawQueue.RestoreOffset();
|
||||||
|
QuikVertex vertex = ImageVertex;
|
||||||
|
|
||||||
|
vertex.Position = new QVec2(rect.Left, rect.Top);
|
||||||
|
vertex.TextureCoordinates = new QVec2(uvs.Left, uvs.Top);
|
||||||
|
DrawQueue.AddVertex(vertex);
|
||||||
|
|
||||||
|
vertex.Position = new QVec2(rect.Left, rect.Bottom);
|
||||||
|
vertex.TextureCoordinates = new QVec2(uvs.Left, uvs.Bottom);
|
||||||
|
DrawQueue.AddVertex(vertex);
|
||||||
|
|
||||||
|
vertex.Position = new QVec2(rect.Right, rect.Bottom);
|
||||||
|
vertex.TextureCoordinates = new QVec2(uvs.Right, uvs.Bottom);
|
||||||
|
DrawQueue.AddVertex(vertex);
|
||||||
|
|
||||||
|
vertex.Position = new QVec2(rect.Right, rect.Top);
|
||||||
|
vertex.TextureCoordinates = new QVec2(uvs.Right, uvs.Top);
|
||||||
|
DrawQueue.AddVertex(vertex);
|
||||||
|
|
||||||
|
DrawQueue.AddElement(0); DrawQueue.AddElement(2); DrawQueue.AddElement(3);
|
||||||
|
DrawQueue.AddElement(0); DrawQueue.AddElement(1); DrawQueue.AddElement(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawQueue.EndDrawCall();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Image3d(CommandQueue queue, QImage image, int count)
|
||||||
|
{
|
||||||
|
DrawQueue.StartDrawCall(Viewport, image);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
QRectangle rect = (QRectangle)queue.Dequeue();
|
||||||
|
QRectangle uvs = (QRectangle)queue.Dequeue();
|
||||||
|
int layer = (int)queue.Dequeue();
|
||||||
|
|
||||||
|
DrawQueue.RestoreOffset();
|
||||||
|
QuikVertex vertex = ImageVertex;
|
||||||
|
vertex.TextureLayer = layer;
|
||||||
|
|
||||||
|
vertex.Position = new QVec2(rect.Top, rect.Left);
|
||||||
|
vertex.TextureCoordinates = new QVec2(uvs.Top, uvs.Left);
|
||||||
|
DrawQueue.AddVertex(vertex);
|
||||||
|
|
||||||
|
vertex.Position = new QVec2(rect.Bottom, rect.Left);
|
||||||
|
vertex.TextureCoordinates = new QVec2(uvs.Bottom, uvs.Left);
|
||||||
|
DrawQueue.AddVertex(vertex);
|
||||||
|
|
||||||
|
vertex.Position = new QVec2(rect.Bottom, rect.Right);
|
||||||
|
vertex.TextureCoordinates = new QVec2(uvs.Bottom, uvs.Right);
|
||||||
|
DrawQueue.AddVertex(vertex);
|
||||||
|
|
||||||
|
vertex.Position = new QVec2(rect.Top, rect.Right);
|
||||||
|
vertex.TextureCoordinates = new QVec2(uvs.Top, uvs.Right);
|
||||||
|
DrawQueue.AddVertex(vertex);
|
||||||
|
|
||||||
|
DrawQueue.AddElement(0); DrawQueue.AddElement(2); DrawQueue.AddElement(3);
|
||||||
|
DrawQueue.AddElement(0); DrawQueue.AddElement(1); DrawQueue.AddElement(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawQueue.EndDrawCall();
|
||||||
|
}
|
||||||
|
|
||||||
private struct LineInfo
|
private struct LineInfo
|
||||||
{
|
{
|
||||||
public int BaseOffset { get; }
|
public int BaseOffset { get; }
|
||||||
61
Dashboard/res/gl21.frag
Normal file
61
Dashboard/res/gl21.frag
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* 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
Dashboard/res/gl21.vert
Normal file
35
Dashboard/res/gl21.vert
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using Quik.FreeType;
|
|
||||||
using Quik.Media;
|
|
||||||
using Quik.Media.Color;
|
|
||||||
|
|
||||||
namespace Quik.Media.Defaults
|
|
||||||
{
|
|
||||||
public class QFontFreeType : QFont
|
|
||||||
{
|
|
||||||
private MemoryStream ms;
|
|
||||||
private FTFace face;
|
|
||||||
|
|
||||||
public override FontInfo Info => 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override QFontPage RasterizePage(int codepage, float size, in FontRasterizerOptions options)
|
|
||||||
{
|
|
||||||
FT.SetCharSize(face, 0, (long)Math.Round(64*size), 0, (uint)Math.Round(options.Resolution));
|
|
||||||
QGlyphMetrics[] allMetrics = new QGlyphMetrics[256];
|
|
||||||
|
|
||||||
// Figure out the map size needed.
|
|
||||||
int pixels = 0;
|
|
||||||
for (int i = 0; i < 256; i++)
|
|
||||||
{
|
|
||||||
uint index = FT.GetCharIndex(face, (ulong)(codepage + i));
|
|
||||||
FT.LoadGlyph(face, index, FTLoadFlags.Default);
|
|
||||||
|
|
||||||
ref readonly FTGlyphMetrics metrics = ref face.Glyph.Metrics;
|
|
||||||
allMetrics[i] = new QGlyphMetrics(
|
|
||||||
codepage + i,
|
|
||||||
new QVec2(metrics.Width, metrics.Height),
|
|
||||||
new QVec2(metrics.HorizontalBearingX/64f, metrics.HorizontalBearingY/64f),
|
|
||||||
new QVec2(metrics.VerticalBearingX/64f, metrics.VerticalBearingY/64f),
|
|
||||||
new QVec2(metrics.HorizontalAdvance/64f, metrics.VerticalAdvance/64f)
|
|
||||||
);
|
|
||||||
pixels = (int)Math.Max(pixels, Math.Max(face.Glyph.Metrics.Width/64f, face.Glyph.Metrics.Height/64f));
|
|
||||||
}
|
|
||||||
|
|
||||||
int bits = Math.ILogB(pixels);
|
|
||||||
if (1 << bits != pixels)
|
|
||||||
{
|
|
||||||
pixels = 1 << bits + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we can create a bitmap and render our glyphs.
|
|
||||||
|
|
||||||
QImageBuffer buffer = new QImageBuffer(QImageFormat.RedU8, (int)pixels, (int)pixels, 256);
|
|
||||||
|
|
||||||
for (int i = 0; i < 256; i++)
|
|
||||||
{
|
|
||||||
uint index = FT.GetCharIndex(face, (ulong)(codepage + i));
|
|
||||||
FT.LoadGlyph(face, index, FTLoadFlags.Default);
|
|
||||||
FT.RenderGlyph(face.Glyph, options.Sdf ? FTRenderMode.Sdf : FTRenderMode.Mono);
|
|
||||||
|
|
||||||
ref readonly FTBitmap bitmap = ref face.Glyph.Bitmap;
|
|
||||||
|
|
||||||
buffer.LockBits3d(out QImageLock dst, QImageLockOptions.Default, i);
|
|
||||||
|
|
||||||
if (bitmap.Buffer != IntPtr.Zero) unsafe
|
|
||||||
{
|
|
||||||
for (int j = 0; j < bitmap.Rows; j++)
|
|
||||||
{
|
|
||||||
Buffer.MemoryCopy(
|
|
||||||
(byte*)bitmap.Buffer + (j * bitmap.Pitch),
|
|
||||||
(byte*)dst.ImagePtr + (j * dst.Width),
|
|
||||||
dst.Width,
|
|
||||||
bitmap.Width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.UnlockBits();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new QFontPage(this, codepage, size, options, buffer, allMetrics);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
base.Dispose(disposing);
|
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
ms.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
FT.DoneFace(face);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FTLibrary Ft => FTProvider.Ft;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
|
||||||
<LanguageVersion>7.3</LanguageVersion>
|
|
||||||
<Nullable>disable</Nullable>
|
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Quik\Quik.csproj" />
|
|
||||||
<ProjectReference Include="..\Quik.StbImage\Quik.StbImage.csproj" />
|
|
||||||
<ProjectReference Include="..\Quik.FreeType\Quik.FreeType.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using OpenTK.Windowing.Desktop;
|
|
||||||
using OpenTK.Windowing.GraphicsLibraryFramework;
|
|
||||||
using Quik.CommandMachine;
|
|
||||||
using Quik.Media;
|
|
||||||
using Quik.OpenGL;
|
|
||||||
using Quik.PAL;
|
|
||||||
|
|
||||||
namespace Quik.OpenTK
|
|
||||||
{
|
|
||||||
public class OpenTKPlatform : IQuikPlatform
|
|
||||||
{
|
|
||||||
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; }
|
|
||||||
|
|
||||||
public event EventHandler EventRaised;
|
|
||||||
|
|
||||||
public NativeWindowSettings DefaultSettings { get; set; } = NativeWindowSettings.Default;
|
|
||||||
|
|
||||||
public IReadOnlyList<OpenTKPort> Ports => _ports;
|
|
||||||
|
|
||||||
private bool IsGLInitialized = false;
|
|
||||||
|
|
||||||
public IQuikPortHandle 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(IQuikPortHandle port) => ((OpenTKPort)port).Dispose();
|
|
||||||
|
|
||||||
public string PortGetTitle(IQuikPortHandle port) => ((OpenTKPort)port).Title;
|
|
||||||
|
|
||||||
public void PortSetTitle(IQuikPortHandle port, string title) => ((OpenTKPort)port).Title = title;
|
|
||||||
|
|
||||||
public QVec2 PortGetSize(IQuikPortHandle port) => ((OpenTKPort)port).Size;
|
|
||||||
|
|
||||||
public void PortSetSize(IQuikPortHandle port, QVec2 size) => ((OpenTKPort)port).Size = size;
|
|
||||||
|
|
||||||
public QVec2 PortGetPosition(IQuikPortHandle port) => ((OpenTKPort)port).Position;
|
|
||||||
|
|
||||||
public void PortSetPosition(IQuikPortHandle port, QVec2 position) => ((OpenTKPort)port).Position = position;
|
|
||||||
|
|
||||||
public bool PortIsValid(IQuikPortHandle port) => ((OpenTKPort)port).IsValid;
|
|
||||||
|
|
||||||
public void PortSubscribeEvent(IQuikPortHandle port, EventHandler handler) => ((OpenTKPort)port).EventRaised += handler;
|
|
||||||
|
|
||||||
public void PortUnsubscribeEvent(IQuikPortHandle port, EventHandler handler) => ((OpenTKPort)port).EventRaised -= handler;
|
|
||||||
|
|
||||||
public void PortFocus(IQuikPortHandle port) => ((OpenTKPort)port).Focus();
|
|
||||||
|
|
||||||
public void PortShow(IQuikPortHandle port, bool shown = true) => ((OpenTKPort)port).Show(shown);
|
|
||||||
|
|
||||||
public void PortPaint(IQuikPortHandle port, CommandList commands) => ((OpenTKPort)port).Paint(commands);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
84
Quik.sln
84
Quik.sln
@@ -1,84 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 17
|
|
||||||
VisualStudioVersion = 17.9.34701.34
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Quik", "Quik\Quik.csproj", "{B86B2B99-DAE4-43CE-A040-1D8E143B94A7}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Quik.OpenTK", "Quik.OpenTK\Quik.OpenTK.csproj", "{586E5E28-1D07-4CC2-B04F-0BC420564F57}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{AE05ADE5-A809-479F-97D5-BEAFE7604285}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuikDemo", "tests\QuikDemo\QuikDemo.csproj", "{79CBF97F-4884-4692-94FB-75DDEB61E26F}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Quik.Media.Defaults", "Quik.Media.Defaults\Quik.Media.Defaults.csproj", "{B517D2BF-CB9D-4448-BE50-EA85E100EB47}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Debug|x64 = Debug|x64
|
|
||||||
Debug|x86 = Debug|x86
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
Release|x64 = Release|x64
|
|
||||||
Release|x86 = Release|x86
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{B86B2B99-DAE4-43CE-A040-1D8E143B94A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{B86B2B99-DAE4-43CE-A040-1D8E143B94A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{B86B2B99-DAE4-43CE-A040-1D8E143B94A7}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{B86B2B99-DAE4-43CE-A040-1D8E143B94A7}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{B86B2B99-DAE4-43CE-A040-1D8E143B94A7}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{B86B2B99-DAE4-43CE-A040-1D8E143B94A7}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{B86B2B99-DAE4-43CE-A040-1D8E143B94A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{B86B2B99-DAE4-43CE-A040-1D8E143B94A7}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{B86B2B99-DAE4-43CE-A040-1D8E143B94A7}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{B86B2B99-DAE4-43CE-A040-1D8E143B94A7}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{B86B2B99-DAE4-43CE-A040-1D8E143B94A7}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{B86B2B99-DAE4-43CE-A040-1D8E143B94A7}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{586E5E28-1D07-4CC2-B04F-0BC420564F57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{586E5E28-1D07-4CC2-B04F-0BC420564F57}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{586E5E28-1D07-4CC2-B04F-0BC420564F57}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{586E5E28-1D07-4CC2-B04F-0BC420564F57}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{586E5E28-1D07-4CC2-B04F-0BC420564F57}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{586E5E28-1D07-4CC2-B04F-0BC420564F57}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{586E5E28-1D07-4CC2-B04F-0BC420564F57}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{586E5E28-1D07-4CC2-B04F-0BC420564F57}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{586E5E28-1D07-4CC2-B04F-0BC420564F57}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{586E5E28-1D07-4CC2-B04F-0BC420564F57}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{586E5E28-1D07-4CC2-B04F-0BC420564F57}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{586E5E28-1D07-4CC2-B04F-0BC420564F57}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{79CBF97F-4884-4692-94FB-75DDEB61E26F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{79CBF97F-4884-4692-94FB-75DDEB61E26F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{79CBF97F-4884-4692-94FB-75DDEB61E26F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{79CBF97F-4884-4692-94FB-75DDEB61E26F}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{79CBF97F-4884-4692-94FB-75DDEB61E26F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{79CBF97F-4884-4692-94FB-75DDEB61E26F}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{79CBF97F-4884-4692-94FB-75DDEB61E26F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{79CBF97F-4884-4692-94FB-75DDEB61E26F}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{79CBF97F-4884-4692-94FB-75DDEB61E26F}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{79CBF97F-4884-4692-94FB-75DDEB61E26F}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{79CBF97F-4884-4692-94FB-75DDEB61E26F}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{79CBF97F-4884-4692-94FB-75DDEB61E26F}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{B517D2BF-CB9D-4448-BE50-EA85E100EB47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{B517D2BF-CB9D-4448-BE50-EA85E100EB47}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{B517D2BF-CB9D-4448-BE50-EA85E100EB47}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{B517D2BF-CB9D-4448-BE50-EA85E100EB47}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{B517D2BF-CB9D-4448-BE50-EA85E100EB47}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{B517D2BF-CB9D-4448-BE50-EA85E100EB47}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{B517D2BF-CB9D-4448-BE50-EA85E100EB47}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{B517D2BF-CB9D-4448-BE50-EA85E100EB47}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{B517D2BF-CB9D-4448-BE50-EA85E100EB47}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{B517D2BF-CB9D-4448-BE50-EA85E100EB47}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{B517D2BF-CB9D-4448-BE50-EA85E100EB47}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{B517D2BF-CB9D-4448-BE50-EA85E100EB47}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(NestedProjects) = preSolution
|
|
||||||
{79CBF97F-4884-4692-94FB-75DDEB61E26F} = {AE05ADE5-A809-479F-97D5-BEAFE7604285}
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {EF011093-DA56-4E14-B2AB-D565B64F73E1}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
namespace Quik.CommandMachine
|
|
||||||
{
|
|
||||||
public enum FrameType
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Command,
|
|
||||||
IVec1,
|
|
||||||
IVec2,
|
|
||||||
IVec3,
|
|
||||||
IVec4,
|
|
||||||
Vec1,
|
|
||||||
Vec2,
|
|
||||||
Vec3,
|
|
||||||
Vec4,
|
|
||||||
Object,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
|
|
||||||
using System;
|
|
||||||
using Quik.CommandMachine;
|
|
||||||
|
|
||||||
namespace Quik.Controls
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Bases for all UI elements.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class UIBase
|
|
||||||
{
|
|
||||||
public UIBase Parent { get; protected set; }
|
|
||||||
public string Id { get; set; }
|
|
||||||
public QRectangle Bounds { get; set; }
|
|
||||||
|
|
||||||
public QVec2 Position
|
|
||||||
{
|
|
||||||
get => Bounds.Min;
|
|
||||||
set => Bounds = new QRectangle(Bounds.Max - Bounds.Min + value, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public QVec2 Size
|
|
||||||
{
|
|
||||||
get => Bounds.Max - Bounds.Min;
|
|
||||||
set => Bounds = new QRectangle(value + Bounds.Min, Bounds.Min);
|
|
||||||
}
|
|
||||||
|
|
||||||
public QRectangle AbsoluteBounds
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (Parent == null)
|
|
||||||
{
|
|
||||||
return Bounds;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new QRectangle(Bounds.Max + Parent.Position, Bounds.Min + Parent.Position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public 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.PopZ();
|
|
||||||
cmd.PopViewport();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Paint(CommandList cmd)
|
|
||||||
{
|
|
||||||
PaintBegin(cmd);
|
|
||||||
PaintEnd(cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Quik.Media;
|
|
||||||
|
|
||||||
namespace Quik
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interface for texture instances.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class QuikTexture : IEquatable<QuikTexture>, IDisposable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Width of the texture.
|
|
||||||
/// </summary>
|
|
||||||
public abstract int Width { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Height of the texture.
|
|
||||||
/// </summary>
|
|
||||||
public abstract int Height { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// True if the texture can have mipmaps.
|
|
||||||
/// </summary>
|
|
||||||
public abstract bool Mipmaps { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates whether this texture contains a signed distance field.
|
|
||||||
/// </summary>
|
|
||||||
public bool SignedDistanceField { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates whether this texture has premultiplied alpha.
|
|
||||||
/// </summary>
|
|
||||||
public bool PreMultipled { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Upload texture data.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data">Pointer to data.</param>
|
|
||||||
/// <param name="format">Color format of the data.</param>
|
|
||||||
/// <param name="size">Size of the texture data.</param>
|
|
||||||
/// <param name="level">Mip level.</param>
|
|
||||||
/// <param name="alignment">Pixel alignment. Expected to be 1, 2, 4, or 8.</param>
|
|
||||||
public abstract void Image(IntPtr data, QImageFormat format, QVec2 size, int level, int alignment = 4);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Upload texture data.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data">Pointer to data.</param>
|
|
||||||
/// <param name="format">Color format for the data.</param>
|
|
||||||
/// <param name="location">Location of the data in the texture.</param>
|
|
||||||
/// <param name="level">Mip level.</param>
|
|
||||||
/// <param name="alignment">Pixel alignment. Expected to be 1, 2, 4, or 8.</param>
|
|
||||||
public abstract void SubImage(IntPtr data, QImageFormat format, QRectangle location, int level, int alignment = 4);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generate the mip maps for the texture.
|
|
||||||
/// </summary>
|
|
||||||
public abstract void GenerateMipMaps();
|
|
||||||
|
|
||||||
public virtual bool Equals(QuikTexture other)
|
|
||||||
{
|
|
||||||
return base.Equals(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() => Dispose(true);
|
|
||||||
protected virtual void Dispose(bool isDisposing) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
using Quik.Media;
|
|
||||||
namespace Quik
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interface for QUIK texture managers.
|
|
||||||
/// </summary>
|
|
||||||
public interface IQuikTextureManager
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The context that owns the texture manager.
|
|
||||||
/// </summary>
|
|
||||||
// QuikContext Context { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a texture.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="size">Size of the texture.</param>
|
|
||||||
/// <param name="mipmaps">True in order to allow mipmaps.</param>
|
|
||||||
/// <param name="format">The color format of the internal texture.</param>
|
|
||||||
/// <returns>Handle to a texture object.</returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// All parameters are hints. If there is a situation where the texture does not
|
|
||||||
/// have mip levels, or format cannot be specified, ignore these parameters.
|
|
||||||
/// </remarks>
|
|
||||||
QuikTexture CreateTexture(QVec2 size, bool mipmaps, QImageFormat format);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A function called on context clear. (useful for discarding old textures)
|
|
||||||
/// </summary>
|
|
||||||
void Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Quik.Media
|
|
||||||
{
|
|
||||||
public class QImageBuffer : QImage
|
|
||||||
{
|
|
||||||
private byte[] buffer;
|
|
||||||
GCHandle handle;
|
|
||||||
|
|
||||||
public override QImageFormat InternalFormat { get; }
|
|
||||||
public override int Width { get; }
|
|
||||||
public override int Height { get; }
|
|
||||||
public override int Depth { get; }
|
|
||||||
|
|
||||||
public QImageBuffer(QImageFormat format, int width, int height, int depth = 1)
|
|
||||||
{
|
|
||||||
InternalFormat = format;
|
|
||||||
Width = width;
|
|
||||||
Height = height;
|
|
||||||
Depth = depth;
|
|
||||||
|
|
||||||
buffer = new byte[width * height * depth * format.BytesPerPixel()];
|
|
||||||
}
|
|
||||||
~QImageBuffer()
|
|
||||||
{
|
|
||||||
Dispose(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private QImageLock Lock()
|
|
||||||
{
|
|
||||||
handle.Free();
|
|
||||||
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
|
||||||
IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
|
|
||||||
return new QImageLock(InternalFormat, Width, Height, Depth, ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (handle.IsAllocated) handle.Free();
|
|
||||||
buffer = null;
|
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void LockBits2d(out QImageLock imageLock, QImageLockOptions options)
|
|
||||||
{
|
|
||||||
if (options.Format != options.Format) throw new InvalidOperationException("This image type cannot be converted.");
|
|
||||||
if (Depth > 1) throw new InvalidOperationException("This texture has a depth component.");
|
|
||||||
|
|
||||||
UnlockBits();
|
|
||||||
|
|
||||||
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
|
||||||
IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
|
|
||||||
imageLock = new QImageLock(InternalFormat, Width, Height, Depth, ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void LockBits3d(out QImageLock imageLock, QImageLockOptions options)
|
|
||||||
{
|
|
||||||
if (options.Format != options.Format) throw new InvalidOperationException("This image type cannot be converted.");
|
|
||||||
UnlockBits();
|
|
||||||
|
|
||||||
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
|
||||||
IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
|
|
||||||
imageLock = new QImageLock(InternalFormat, Width, Height, Depth, ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void LockBits3d(out QImageLock imageLock, QImageLockOptions options, int depth)
|
|
||||||
{
|
|
||||||
if (options.Format != options.Format) throw new InvalidOperationException("This image type cannot be converted.");
|
|
||||||
if (depth < 0 || depth > Depth)
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(depth), "Depth must be in range.");
|
|
||||||
|
|
||||||
UnlockBits();
|
|
||||||
|
|
||||||
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
|
||||||
IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
|
|
||||||
imageLock = new QImageLock(
|
|
||||||
InternalFormat,
|
|
||||||
Width,
|
|
||||||
Height,
|
|
||||||
1,
|
|
||||||
ptr + (depth * Width * Height * InternalFormat.BytesPerPixel()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void UnlockBits()
|
|
||||||
{
|
|
||||||
if (handle.IsAllocated)
|
|
||||||
handle.Free();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Quik.Media
|
|
||||||
{
|
|
||||||
public struct FontInfo : IEquatable<FontInfo>
|
|
||||||
{
|
|
||||||
public string Family { get; }
|
|
||||||
public FontStyle Style { get; }
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return $"{Family} {Style}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return Family.GetHashCode() ^
|
|
||||||
(Style.GetHashCode() * 3976061);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator==(FontInfo a, FontInfo b)
|
|
||||||
{
|
|
||||||
return (a.Style == b.Style) &&
|
|
||||||
(a.Family == a.Family);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator!=(FontInfo a, FontInfo b)
|
|
||||||
{
|
|
||||||
return (a.Style != b.Style) ||
|
|
||||||
(a.Family != b.Family);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(FontInfo other)
|
|
||||||
{
|
|
||||||
return this == other;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
return (obj.GetType() == typeof(FontInfo)) &&
|
|
||||||
this == (FontInfo)obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Quik.Media
|
|
||||||
{
|
|
||||||
[Flags]
|
|
||||||
public enum FontStyle
|
|
||||||
{
|
|
||||||
Italic = 1 << 0,
|
|
||||||
Bold = 1 << 1,
|
|
||||||
|
|
||||||
Normal = 0,
|
|
||||||
BoldItalic = Italic | Bold,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Quik.Media
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Abstract class that represents a font.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class QFont : IDisposable
|
|
||||||
{
|
|
||||||
public abstract FontInfo Info { get; }
|
|
||||||
public string Family => Info.Family;
|
|
||||||
public FontStyle Style => Info.Style;
|
|
||||||
|
|
||||||
public abstract bool HasRune(int rune);
|
|
||||||
public abstract QFontPage RasterizePage(int codepage, float size, in FontRasterizerOptions options);
|
|
||||||
public QFontPage RasterizePage(int codepage, float size) => RasterizePage(codepage, size, FontRasterizerOptions.Default);
|
|
||||||
|
|
||||||
// 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 FontRasterizerOptions
|
|
||||||
{
|
|
||||||
public float Resolution { get; set; }
|
|
||||||
public bool Sdf { get; set; }
|
|
||||||
|
|
||||||
public static readonly FontRasterizerOptions Default = new FontRasterizerOptions()
|
|
||||||
{
|
|
||||||
Resolution = 96.0f,
|
|
||||||
Sdf = true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public class QFontPage : IDisposable
|
|
||||||
{
|
|
||||||
public QFont Font { get; }
|
|
||||||
public int CodePage { get; }
|
|
||||||
public float Size { get; }
|
|
||||||
public virtual QImage Image { get; } = null;
|
|
||||||
public virtual QGlyphMetrics[] Metrics { get; } = Array.Empty<QGlyphMetrics>();
|
|
||||||
|
|
||||||
public FontRasterizerOptions Options { get; }
|
|
||||||
public float Resolution => Options.Resolution;
|
|
||||||
public bool Sdf => Options.Sdf;
|
|
||||||
|
|
||||||
public void Dispose() => DisposeInternal(false);
|
|
||||||
|
|
||||||
protected QFontPage(QFont font, int codepage, float size, in FontRasterizerOptions options)
|
|
||||||
{
|
|
||||||
Font = font;
|
|
||||||
CodePage = codepage;
|
|
||||||
Size = size;
|
|
||||||
Options = options;
|
|
||||||
}
|
|
||||||
public QFontPage(QFont font, int codepage, float size, in FontRasterizerOptions options, QImage image, QGlyphMetrics[] metrics)
|
|
||||||
: this(font, codepage, size, options)
|
|
||||||
{
|
|
||||||
Image = image;
|
|
||||||
Metrics = metrics;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool isDisposed = false;
|
|
||||||
private void DisposeInternal(bool disposing)
|
|
||||||
{
|
|
||||||
if (isDisposed) return;
|
|
||||||
|
|
||||||
Dispose(disposing);
|
|
||||||
isDisposed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
Image?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Quik.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 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Quik.CommandMachine;
|
|
||||||
using Quik.Media;
|
|
||||||
|
|
||||||
namespace Quik.PAL
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An empty interface to statically type Quik port handles.
|
|
||||||
/// </summary>
|
|
||||||
public interface IQuikPortHandle
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The primary primary platform abstraction interface for Quik hosts.
|
|
||||||
/// </summary>
|
|
||||||
public interface IQuikPlatform : 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>
|
|
||||||
IQuikPortHandle CreatePort();
|
|
||||||
void DestroyPort(IQuikPortHandle port);
|
|
||||||
string PortGetTitle(IQuikPortHandle port);
|
|
||||||
void PortSetTitle(IQuikPortHandle port, string title);
|
|
||||||
QVec2 PortGetSize(IQuikPortHandle port);
|
|
||||||
void PortSetSize(IQuikPortHandle port, QVec2 size);
|
|
||||||
QVec2 PortGetPosition(IQuikPortHandle port);
|
|
||||||
void PortSetPosition(IQuikPortHandle port, QVec2 position);
|
|
||||||
bool PortIsValid(IQuikPortHandle port);
|
|
||||||
void PortSubscribeEvent(IQuikPortHandle port, EventHandler handler);
|
|
||||||
void PortUnsubscribeEvent(IQuikPortHandle port, EventHandler handler);
|
|
||||||
void PortFocus(IQuikPortHandle port);
|
|
||||||
void PortShow(IQuikPortHandle port, bool shown = true);
|
|
||||||
void PortPaint(IQuikPortHandle port, CommandList commands);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
/**
|
|
||||||
* QUIK: User Interface Kit
|
|
||||||
* Copyright (C) 2023 Halit Utku Maden, et al.
|
|
||||||
*/
|
|
||||||
#version 120
|
|
||||||
|
|
||||||
varying vec2 fv2TexPos;
|
|
||||||
varying vec4 fv4Color;
|
|
||||||
|
|
||||||
uniform int iEnableSdf;
|
|
||||||
uniform int iEnableTexture;
|
|
||||||
uniform int iAlphaDiscard;
|
|
||||||
uniform float fSdfThreshold;
|
|
||||||
uniform sampler2D txTexture;
|
|
||||||
|
|
||||||
void main(void)
|
|
||||||
{
|
|
||||||
vec4 albedo = fv4Color;
|
|
||||||
|
|
||||||
if (iEnableTexture != 0)
|
|
||||||
{
|
|
||||||
vec4 value = texture2D(txTexture, fv2TexPos);
|
|
||||||
|
|
||||||
if (iEnableSdf != 0)
|
|
||||||
{
|
|
||||||
float a = max(value.r, value.a);
|
|
||||||
|
|
||||||
value = (a >= fSdfThreshold) ? vec4(1,1,1,1) : vec4(0,0,0,0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iAlphaDiscard != 0 && value.a == 0.0)
|
|
||||||
{
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
albedo = albedo * value;
|
|
||||||
}
|
|
||||||
|
|
||||||
gl_FragColor = albedo;
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
/**
|
|
||||||
* QUIK: User Interface Kit
|
|
||||||
* Copyright (C) 2023 Halit Utku Maden, et al.
|
|
||||||
*/
|
|
||||||
#version 120
|
|
||||||
|
|
||||||
attribute vec2 v2Position; /**< The vertex position.*/
|
|
||||||
attribute int iZIndex; /**< The z index. */
|
|
||||||
attribute vec2 v2TexPos; /**< The texture coorindates. */
|
|
||||||
attribute vec4 v4Color; /**< The vertex color. */
|
|
||||||
|
|
||||||
varying vec2 fv2TexPos;
|
|
||||||
varying vec4 fv4Color;
|
|
||||||
|
|
||||||
uniform mat4 m4Transforms; /**< The view matrix. */
|
|
||||||
uniform int iMaxZ; /**< Highest Z coordinate. */
|
|
||||||
|
|
||||||
void main(void)
|
|
||||||
{
|
|
||||||
vec4 v = vec4(v2Position, float(iZIndex)/float(iMaxZ), 1);
|
|
||||||
gl_Position = v * m4Transforms;
|
|
||||||
|
|
||||||
fv2TexPos = v2TexPos;
|
|
||||||
fv4Color = v4Color;
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
Quik/lib
|
|
||||||
========
|
|
||||||
This is where the git submodules of 3rd party native dependencies are supposed
|
|
||||||
to end up at. Also note `lib/quik` is where all common headers go for building
|
|
||||||
these libraries and their distributions.
|
|
||||||
Submodule lib/freetype deleted from 86bc8a9505
1
lib/stb
1
lib/stb
Submodule lib/stb deleted from 5736b15f7e
@@ -1,9 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\Quik\Quik.csproj" />
|
<ProjectReference Include="..\..\Dashboard\Dashboard.csproj" />
|
||||||
<ProjectReference Include="..\..\Quik.Media.Defaults\Quik.Media.Defaults.csproj" />
|
<ProjectReference Include="..\..\Dashboard.Media.Defaults\Dashboard.Media.Defaults.csproj" />
|
||||||
<ProjectReference Include="..\..\Quik.OpenTK\Quik.OpenTK.csproj" />
|
<ProjectReference Include="..\..\Dashboard.OpenTK\Dashboard.OpenTK.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
44
tests/Dashboard.Demo/Program.cs
Normal file
44
tests/Dashboard.Demo/Program.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
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,28 +0,0 @@
|
|||||||
using Quik;
|
|
||||||
using Quik.CommandMachine;
|
|
||||||
using Quik.Controls;
|
|
||||||
using Quik.OpenTK;
|
|
||||||
|
|
||||||
|
|
||||||
namespace QuikDemo
|
|
||||||
{
|
|
||||||
public static class Program
|
|
||||||
{
|
|
||||||
public static readonly QuikApplication Application = new QuikApplication(new OpenTKPlatform());
|
|
||||||
|
|
||||||
public static void Main(string[] args)
|
|
||||||
{
|
|
||||||
Application.Run(new EmptyView());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class EmptyView : View
|
|
||||||
{
|
|
||||||
protected override void PaintBegin(CommandQueue cmd)
|
|
||||||
{
|
|
||||||
base.PaintBegin(cmd);
|
|
||||||
|
|
||||||
cmd.Rectangle(new QRectangle(0, 0, 16, 16));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user