Compare commits
78 Commits
ae0c9e742c
...
db-refacto
| Author | SHA1 | Date | |
|---|---|---|---|
| c6a9dd8008 | |||
| ff3b3ee961 | |||
| 9b6a15c6ec | |||
| 50552914c1 | |||
| d959c42e99 | |||
| 9b2f0859e5 | |||
| 831c93b916 | |||
| 42782b8a71 | |||
| 1301868269 | |||
| 21591c3d11 | |||
| 3856b3c66e | |||
| 92fb8f06a7 | |||
| 468648de95 | |||
| 3f1875252e | |||
| 65609bf685 | |||
| a95ddb46ad | |||
| 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 | |||
| a1573d3786 | |||
| 20c126fb88 | |||
| 7ce474d92a | |||
| 2eb5663ee9 | |||
| 09ce8d3229 | |||
|
|
98aee237bd | ||
|
|
d8beb2a274 | ||
|
|
a8d805b461 | ||
| 4dff6eba91 | |||
| 9c9efc6eeb | |||
| 51e9018a22 | |||
| 032a38e13b | |||
|
cea243a3b8
|
|||
|
6240f5921b
|
|||
|
959788563f
|
|||
|
118b50cee2
|
|||
|
1f6a3a55e1
|
|||
|
ac0a70cefc
|
|||
|
845ed1c27a
|
|||
|
4a35f18737
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@ obj/
|
|||||||
riderModule.iml
|
riderModule.iml
|
||||||
/_ReSharper.Caches/
|
/_ReSharper.Caches/
|
||||||
.idea
|
.idea
|
||||||
|
.vs
|
||||||
.vscode
|
.vscode
|
||||||
nuget_repo
|
nuget_repo
|
||||||
**/out
|
**/out
|
||||||
|
|||||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -1,6 +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
|
|
||||||
27
Dashboard.BlurgText/BlurgCommand.cs
Normal file
27
Dashboard.BlurgText/BlurgCommand.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using BlurgText;
|
||||||
|
using Dashboard.ImmediateDraw;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Dashboard.BlurgText
|
||||||
|
{
|
||||||
|
public static class BlurgCommand
|
||||||
|
{
|
||||||
|
public static void PutBlurgText(this DrawList list, DashboardBlurg blurg, BlurgResult result, Vector2 origin)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < result.Count; i++)
|
||||||
|
{
|
||||||
|
BlurgRect rect = result[i];
|
||||||
|
|
||||||
|
Rectangle pos = new Rectangle()
|
||||||
|
{
|
||||||
|
Min = origin + new Vector2(rect.X, rect.Y),
|
||||||
|
Size = new Vector2(rect.Width, rect.Height)
|
||||||
|
};
|
||||||
|
|
||||||
|
Rectangle uv = new Rectangle(rect.U1, rect.V1, rect.U0, rect.V0);
|
||||||
|
|
||||||
|
list.Image(blurg.Images[(int)rect.UserData], pos, uv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
Dashboard.BlurgText/Dashboard.BlurgText.csproj
Normal file
14
Dashboard.BlurgText/Dashboard.BlurgText.csproj
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Dashboard\Dashboard.csproj" />
|
||||||
|
<PackageReference Include="BlurgText" Version="0.1.0-nightly-4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
63
Dashboard.BlurgText/DashboardBlurg.cs
Normal file
63
Dashboard.BlurgText/DashboardBlurg.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
|
||||||
|
using BlurgText;
|
||||||
|
using Dashboard.Media;
|
||||||
|
using Dashboard.Media.Color;
|
||||||
|
|
||||||
|
namespace Dashboard.BlurgText
|
||||||
|
{
|
||||||
|
public class DashboardBlurg : IDisposable
|
||||||
|
{
|
||||||
|
private readonly List<QImageBuffer> images = new List<QImageBuffer>();
|
||||||
|
|
||||||
|
public Blurg Blurg { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<QImageBuffer> Images => images.AsReadOnly();
|
||||||
|
|
||||||
|
public DashboardBlurg()
|
||||||
|
{
|
||||||
|
Blurg = new Blurg(TextureAllocationCallback, TextureUpdateCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private nint TextureAllocationCallback(int width, int height)
|
||||||
|
{
|
||||||
|
QImageBuffer image = new QImageBuffer(ImageFormat.RgbaU8, width, height);
|
||||||
|
images.Add(image);
|
||||||
|
return images.Count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TextureUpdateCallback(nint userData, nint buffer, int x, int y, int width, int height)
|
||||||
|
{
|
||||||
|
if (width == 0 || height == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QImageLock src = new QImageLock(ImageFormat.RgbaU8, width, height, 1, buffer);
|
||||||
|
QImageBuffer image = images[(int)userData];
|
||||||
|
image.LockBits2d(out QImageLock dest, QImageLockOptions.Default);
|
||||||
|
|
||||||
|
src.CopyTo(dest, x, y);
|
||||||
|
image.UnlockBits();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _isDisposed = false;
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_isDisposed) return;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
Blurg.Dispose();
|
||||||
|
|
||||||
|
foreach (QImageBuffer image in images)
|
||||||
|
{
|
||||||
|
image.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
images.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDisposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => Dispose(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
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";
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Dashboard.Media.Defaults/FTProvider.cs
Normal file
16
Dashboard.Media.Defaults/FTProvider.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using ReFuel.FreeType;
|
||||||
|
|
||||||
|
namespace Dashboard.Media.Defaults
|
||||||
|
{
|
||||||
|
public static class FTProvider
|
||||||
|
{
|
||||||
|
private static FTLibrary _ft;
|
||||||
|
public static FTLibrary Ft => _ft;
|
||||||
|
|
||||||
|
static FTProvider()
|
||||||
|
{
|
||||||
|
FT.InitFreeType(out _ft);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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.Fonts;
|
||||||
|
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.Fonts;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
84
Dashboard.Media.Defaults/FontFreeType.cs
Normal file
84
Dashboard.Media.Defaults/FontFreeType.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.IO;
|
||||||
|
using ReFuel.FreeType;
|
||||||
|
using Dashboard.Media.Color;
|
||||||
|
using Dashboard.Media.Fonts;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Dashboard.Media.Defaults
|
||||||
|
{
|
||||||
|
public class FontFreeType : Font
|
||||||
|
{
|
||||||
|
private MemoryStream ms;
|
||||||
|
private FTFace face;
|
||||||
|
|
||||||
|
public override FontFace Face => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public FontFreeType(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 Image Render(out GlyphMetrics 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 GlyphMetrics(codepoint,
|
||||||
|
new Vector2(ftmetrics.Width/64f, ftmetrics.Height/64f),
|
||||||
|
new Vector2(ftmetrics.HorizontalBearingX/64f, ftmetrics.HorizontalBearingY/64f),
|
||||||
|
new Vector2(ftmetrics.VerticalBearingX/64f, ftmetrics.VerticalBearingY/64f),
|
||||||
|
new Vector2(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(ImageFormat.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
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 Font font)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
font = new FontFreeType(stream);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
font = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +1,25 @@
|
|||||||
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.Stb
|
namespace Dashboard.Media.Defaults
|
||||||
{
|
{
|
||||||
public unsafe class QImageStbi : QImage
|
public unsafe class ImageStbi : Image
|
||||||
{
|
{
|
||||||
private readonly StbImage image;
|
private readonly StbImage image;
|
||||||
private ImageBuffer 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 QImageFormat InternalFormat => Stb2QImageFormat(image.Format);
|
public override bool IsSdf => isSdf;
|
||||||
|
public override ImageFormat InternalFormat => Stb2QImageFormat(image.Format);
|
||||||
|
|
||||||
public QImageStbi(Stream source)
|
public ImageStbi(Stream source)
|
||||||
{
|
{
|
||||||
// According to the stbi documentation, only a specific type of PNG
|
// According to the stbi documentation, only a specific type of PNG
|
||||||
// files are premultiplied out of the box (iPhone PNG). Take the
|
// files are premultiplied out of the box (iPhone PNG). Take the
|
||||||
@@ -30,15 +30,15 @@ namespace Quik.Media.Stb
|
|||||||
image = StbImage.Load(source);
|
image = StbImage.Load(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static QImageFormat Stb2QImageFormat(StbiImageFormat src)
|
public static ImageFormat Stb2QImageFormat(StbiImageFormat src)
|
||||||
{
|
{
|
||||||
switch (src)
|
switch (src)
|
||||||
{
|
{
|
||||||
case StbiImageFormat.Grey: return QImageFormat.RedU8;
|
case StbiImageFormat.Grey: return ImageFormat.RedU8;
|
||||||
case StbiImageFormat.Rgb: return QImageFormat.RgbU8;
|
case StbiImageFormat.Rgb: return ImageFormat.RgbU8;
|
||||||
case StbiImageFormat.Rgba: return QImageFormat.RgbaU8;
|
case StbiImageFormat.Rgba: return ImageFormat.RgbaU8;
|
||||||
case StbiImageFormat.GreyAlpha: return QImageFormat.RaU8;
|
case StbiImageFormat.GreyAlpha: return ImageFormat.RaU8;
|
||||||
default: return QImageFormat.Undefined;
|
default: return ImageFormat.Undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,8 +47,8 @@ namespace Quik.Media.Stb
|
|||||||
if (options.MipLevel > 0) throw new Exception("This image has no mip levels.");
|
if (options.MipLevel > 0) throw new Exception("This image has no mip levels.");
|
||||||
|
|
||||||
buffer?.Dispose();
|
buffer?.Dispose();
|
||||||
buffer = new ImageBuffer(options.Format, Width, Height);
|
buffer = new QImageBuffer(options.Format, Width, Height);
|
||||||
QImageLock dst = buffer.Lock();
|
buffer.LockBits2d(out QImageLock dst, QImageLockOptions.Default);
|
||||||
|
|
||||||
byte *srcPtr = (byte*)image.ImagePointer;
|
byte *srcPtr = (byte*)image.ImagePointer;
|
||||||
QImageLock src = new QImageLock(InternalFormat, Width, Height, 1, (IntPtr)srcPtr);
|
QImageLock src = new QImageLock(InternalFormat, Width, Height, 1, (IntPtr)srcPtr);
|
||||||
@@ -76,7 +76,12 @@ namespace Quik.Media.Stb
|
|||||||
|
|
||||||
public override void UnlockBits()
|
public override void UnlockBits()
|
||||||
{
|
{
|
||||||
buffer.Unlock();
|
buffer.UnlockBits();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SdfHint(bool value = true)
|
||||||
|
{
|
||||||
|
isSdf = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
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.Fonts;
|
||||||
|
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 = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.Fonts;
|
||||||
|
|
||||||
// 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.Stb
|
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.Stb
|
|||||||
{
|
{
|
||||||
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.Stb
|
|||||||
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,16 +2,15 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Nullable>disable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>7.3</LangVersion>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="OpenTK" Version="4.7.4" />
|
<PackageReference Include="OpenTK" Version="4.8.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Quik\Quik.csproj" />
|
<ProjectReference Include="..\Dashboard\Dashboard.csproj" />
|
||||||
<EmbeddedResource Include="glsl\**"/>
|
<EmbeddedResource Include="glsl\**"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
105
Dashboard.OpenTK/OpenTKPlatform.cs
Normal file
105
Dashboard.OpenTK/OpenTKPlatform.cs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
using Dashboard.ImmediateDraw;
|
||||||
|
using Dashboard.Media;
|
||||||
|
using Dashboard.PAL;
|
||||||
|
using OpenTK.Graphics.OpenGL4;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
using OpenTK.Windowing.Desktop;
|
||||||
|
using OpenTK.Windowing.GraphicsLibraryFramework;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Dashboard.OpenTK
|
||||||
|
{
|
||||||
|
public class OpenTKPlatform : IDbPlatform
|
||||||
|
{
|
||||||
|
private readonly List<OpenTKPort> _ports = new List<OpenTKPort>();
|
||||||
|
|
||||||
|
// These shall remain a sad nop for now.
|
||||||
|
public string? Title { get; set; }
|
||||||
|
public Media.Image? 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(new GLFWBindingsContext());
|
||||||
|
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 Vector2 PortGetSize(IDashHandle port) => ((OpenTKPort)port).Size;
|
||||||
|
|
||||||
|
public void PortSetSize(IDashHandle port, Vector2 size) => ((OpenTKPort)port).Size = size;
|
||||||
|
|
||||||
|
public Vector2 PortGetPosition(IDashHandle port) => ((OpenTKPort)port).Position;
|
||||||
|
|
||||||
|
public void PortSetPosition(IDashHandle port, Vector2 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, DrawList commands) => ((OpenTKPort)port).Paint(commands);
|
||||||
|
|
||||||
|
public void GetMaximumImage(out int width, out int height)
|
||||||
|
{
|
||||||
|
GL.GetInteger(GetPName.MaxTextureSize, out int value);
|
||||||
|
width = height = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetMaximumImage(out int width, out int height, out int depth)
|
||||||
|
{
|
||||||
|
GetMaximumImage(out width, out height);
|
||||||
|
GL.GetInteger(GetPName.MaxArrayTextureLayers, out int value);
|
||||||
|
depth = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,19 @@
|
|||||||
using System;
|
using System;
|
||||||
using OpenTK.Mathematics;
|
using OpenTK.Mathematics;
|
||||||
using OpenTK.Windowing.Desktop;
|
using OpenTK.Windowing.Desktop;
|
||||||
using Quik.OpenGL;
|
using OpenTK.Graphics.OpenGL4;
|
||||||
using Quik.CommandMachine;
|
using Dashboard.OpenGL;
|
||||||
using Quik.PAL;
|
using Dashboard.ImmediateDraw;
|
||||||
using Quik.VertexGenerator;
|
using Dashboard.PAL;
|
||||||
|
using Dashboard.VertexGenerator;
|
||||||
|
|
||||||
namespace Quik.OpenTK
|
namespace Dashboard.OpenTK
|
||||||
{
|
{
|
||||||
public class OpenTKPort : IQuikPort
|
public class OpenTKPort : IDashHandle
|
||||||
{
|
{
|
||||||
private readonly NativeWindow _window;
|
private readonly NativeWindow _window;
|
||||||
private readonly GL21Driver _glDriver;
|
private readonly GL21Driver _glDriver;
|
||||||
private readonly VertexGeneratorEngine _vertexEngine;
|
private readonly VertexDrawingEngine _vertexEngine;
|
||||||
|
|
||||||
public string Title
|
public string Title
|
||||||
{
|
{
|
||||||
@@ -20,12 +21,12 @@ namespace Quik.OpenTK
|
|||||||
set => _window.Title = value;
|
set => _window.Title = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QVec2 Size
|
public Vector2 Size
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
Vector2i size = _window.ClientSize;
|
Vector2i size = _window.ClientSize;
|
||||||
return new QVec2(size.X, size.Y);
|
return new Vector2(size.X, size.Y);
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
@@ -35,12 +36,12 @@ namespace Quik.OpenTK
|
|||||||
_window.Size = size;
|
_window.Size = size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public QVec2 Position
|
public Vector2 Position
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
Vector2i location = _window.Location;
|
Vector2i location = _window.Location;
|
||||||
return new QVec2(location.X, location.Y);
|
return new Vector2(location.X, location.Y);
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
@@ -51,13 +52,13 @@ 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)
|
||||||
{
|
{
|
||||||
_window = window;
|
_window = window;
|
||||||
_glDriver = new GL21Driver();
|
_glDriver = new GL21Driver();
|
||||||
_vertexEngine = new VertexGeneratorEngine();
|
_vertexEngine = new VertexDrawingEngine();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Focus()
|
public void Focus()
|
||||||
@@ -65,18 +66,20 @@ namespace Quik.OpenTK
|
|||||||
_window.Focus();
|
_window.Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Paint(CommandQueue queue)
|
public void Paint(DrawList queue)
|
||||||
{
|
{
|
||||||
QRectangle view = new QRectangle(Size, new QVec2(0, 0));
|
Rectangle view = new Rectangle(Size, new Vector2(0, 0));
|
||||||
|
|
||||||
_vertexEngine.Reset();
|
_vertexEngine.Reset();
|
||||||
_vertexEngine.ProcessCommands(new QRectangle(), queue);
|
_vertexEngine.ProcessCommands(view, queue);
|
||||||
|
|
||||||
|
if (!_window.Context.IsCurrent)
|
||||||
_window.Context.MakeCurrent();
|
_window.Context.MakeCurrent();
|
||||||
|
|
||||||
if (!_glDriver.IsInit)
|
if (!_glDriver.IsInit)
|
||||||
_glDriver.Init();
|
_glDriver.Init();
|
||||||
|
|
||||||
|
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
||||||
_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
|
||||||
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
|
||||||
|
}
|
||||||
|
}
|
||||||
86
Dashboard/Controls/ContainerControl.cs
Normal file
86
Dashboard/Controls/ContainerControl.cs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
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 event EventHandler<ChildEventArgs>? ChildAdded;
|
||||||
|
public event EventHandler<ChildEventArgs>? ChildRemoved;
|
||||||
|
|
||||||
|
public void Add(Control item)
|
||||||
|
{
|
||||||
|
children.Add(item);
|
||||||
|
OnChildAdded(new ChildEventArgs(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
foreach (Control child in this)
|
||||||
|
{
|
||||||
|
OnChildRemoved(new ChildEventArgs(child));
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (children.Remove(item))
|
||||||
|
{
|
||||||
|
OnChildRemoved(new ChildEventArgs(item));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void OnChildAdded(ChildEventArgs ea)
|
||||||
|
{
|
||||||
|
Adopt(ea.Child, this);
|
||||||
|
ChildAdded?.Invoke(this, ea);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void OnChildRemoved(ChildEventArgs ea)
|
||||||
|
{
|
||||||
|
Adopt(ea.Child, null);
|
||||||
|
ChildRemoved?.Invoke(this, ea);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return children.GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class ChildEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public Control Child { get; }
|
||||||
|
|
||||||
|
public ChildEventArgs(Control child)
|
||||||
|
{
|
||||||
|
Child = child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
142
Dashboard/Controls/Control.cs
Normal file
142
Dashboard/Controls/Control.cs
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Dashboard.ImmediateDraw;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Dashboard.Controls
|
||||||
|
{
|
||||||
|
public abstract class Control : UIBase
|
||||||
|
{
|
||||||
|
private readonly DrawList drawCommands = new DrawList();
|
||||||
|
|
||||||
|
public string? Id { get; set; }
|
||||||
|
|
||||||
|
public override Vector2 Position
|
||||||
|
{
|
||||||
|
get => base.Position;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
base.Position = value;
|
||||||
|
InvalidateLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
protected ResizedEventArgs? LastResizeEvent { get; private set; }
|
||||||
|
|
||||||
|
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(DrawList cmd);
|
||||||
|
protected abstract void ValidateLayout();
|
||||||
|
|
||||||
|
protected override void PaintBegin(DrawList 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);
|
||||||
|
|
||||||
|
if (this is IEnumerable<Control> children)
|
||||||
|
{
|
||||||
|
foreach (Control child in children)
|
||||||
|
{
|
||||||
|
child.Paint(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnResized(object sender, ResizedEventArgs ea)
|
||||||
|
{
|
||||||
|
base.OnResized(sender, ea);
|
||||||
|
|
||||||
|
LastResizeEvent = ea;
|
||||||
|
InvalidateLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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.ImmediateDraw;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ValidateVisual(DrawList 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, Vector2 offset, Vector2 size, float packedWidth)
|
||||||
|
{
|
||||||
|
Vector2 pointer = offset;
|
||||||
|
|
||||||
|
pointer.X += hstart();
|
||||||
|
|
||||||
|
foreach (Control child in line)
|
||||||
|
{
|
||||||
|
child.Position = pointer;
|
||||||
|
pointer += new Vector2(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/GridBox.cs
Normal file
31
Dashboard/Controls/GridBox.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
using Dashboard.ImmediateDraw;
|
||||||
|
using Dashboard.Layout;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Dashboard.Controls
|
||||||
|
{
|
||||||
|
public class GridBox : ContainerControl
|
||||||
|
{
|
||||||
|
protected override void ValidateLayout()
|
||||||
|
{
|
||||||
|
if (LastResizeEvent != null)
|
||||||
|
{
|
||||||
|
foreach (Control child in this)
|
||||||
|
{
|
||||||
|
GridLayoutAttribute attribute = GridLayoutAttribute.GetGridLayout(child);
|
||||||
|
|
||||||
|
attribute.Evaluate(LastResizeEvent, child.Bounds, out Rectangle newBounds);
|
||||||
|
child.Bounds = newBounds;
|
||||||
|
|
||||||
|
child.InvalidateLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ValidateVisual(DrawList cmd)
|
||||||
|
{
|
||||||
|
cmd.Rectangle(new Rectangle(Size, Vector2.Zero));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
Dashboard/Controls/Label.cs
Normal file
32
Dashboard/Controls/Label.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using Dashboard.ImmediateDraw;
|
||||||
|
using Dashboard.Media;
|
||||||
|
using Dashboard.Typography;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Dashboard.Controls
|
||||||
|
{
|
||||||
|
public class Label : Control
|
||||||
|
{
|
||||||
|
public string Text { get; set; } = string.Empty;
|
||||||
|
public Font? Font { get; set; }
|
||||||
|
public float TextSize { get; set; }
|
||||||
|
public bool AutoSize { get; set; } = true;
|
||||||
|
|
||||||
|
protected override void ValidateLayout()
|
||||||
|
{
|
||||||
|
if (AutoSize)
|
||||||
|
{
|
||||||
|
Vector2 size = Typesetter.MeasureHorizontal(Text, TextSize, Font!);
|
||||||
|
Size = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ValidateVisual(DrawList cmd)
|
||||||
|
{
|
||||||
|
float padding = Padding;
|
||||||
|
Vector2 origin = new Vector2(padding, padding);
|
||||||
|
|
||||||
|
cmd.TypesetHorizontalDirect(Text, origin, TextSize, Font!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
151
Dashboard/Controls/UIBase.cs
Normal file
151
Dashboard/Controls/UIBase.cs
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Dashboard.ImmediateDraw;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Dashboard.Controls
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bases for all UI elements.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class UIBase : IDbAttribute
|
||||||
|
{
|
||||||
|
private Vector2 size;
|
||||||
|
|
||||||
|
public UIBase? Parent { get; protected set; }
|
||||||
|
public Rectangle Bounds
|
||||||
|
{
|
||||||
|
get => new Rectangle(Position + Size, Position);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Size = value.Size;
|
||||||
|
Position = value.Min;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Vector2 Position { get; set; }
|
||||||
|
|
||||||
|
public Vector2 Size
|
||||||
|
{
|
||||||
|
get => size;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Vector2 oldSize = size;
|
||||||
|
size = value;
|
||||||
|
|
||||||
|
OnResized(this, new ResizedEventArgs(size, oldSize));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rectangle AbsoluteBounds
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Parent == null)
|
||||||
|
{
|
||||||
|
return Bounds;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new Rectangle(Bounds.Max + Parent.Position, Bounds.Min + Parent.Position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2 MaximumSize { get; set; } = new Vector2(-1, -1);
|
||||||
|
public Vector2 MinimumSize { get; set; } = new Vector2(-1, -1);
|
||||||
|
|
||||||
|
public bool IsMaximumSizeSet => MaximumSize != new Vector2(-1, -1);
|
||||||
|
public bool IsMinimumSizeSet => MinimumSize != new Vector2(-1, -1);
|
||||||
|
|
||||||
|
public Dictionary<string, object> Attributes { get; } = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
public virtual void NotifyEvent(object? sender, EventArgs args)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void PaintBegin(DrawList cmd)
|
||||||
|
{
|
||||||
|
cmd.PushViewport();
|
||||||
|
cmd.StoreViewport(AbsoluteBounds);
|
||||||
|
cmd.PushZ();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void PaintEnd(DrawList cmd)
|
||||||
|
{
|
||||||
|
cmd.PopViewport();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Paint(DrawList cmd)
|
||||||
|
{
|
||||||
|
PaintBegin(cmd);
|
||||||
|
PaintEnd(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler<ResizedEventArgs>? Resized;
|
||||||
|
public event EventHandler<AdoptedEventArgs>? Adopted;
|
||||||
|
|
||||||
|
public virtual void OnResized(object sender, ResizedEventArgs ea)
|
||||||
|
{
|
||||||
|
Resized?.Invoke(sender, ea);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnAdopted(UIBase? parent)
|
||||||
|
{
|
||||||
|
Adopted?.Invoke(this, new AdoptedEventArgs(parent));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsDisposed { get; private set; } = false;
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
foreach (object userdata in Attributes.Values)
|
||||||
|
{
|
||||||
|
if (userdata is IDisposable disposable)
|
||||||
|
disposable.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DisposeInvoker(bool disposing)
|
||||||
|
{
|
||||||
|
if (IsDisposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IsDisposed = true;
|
||||||
|
|
||||||
|
Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => DisposeInvoker(true);
|
||||||
|
|
||||||
|
protected static void Adopt(UIBase child, UIBase? parent)
|
||||||
|
{
|
||||||
|
child.Parent = parent;
|
||||||
|
child.OnAdopted(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ResizedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public Vector2 NewSize { get; }
|
||||||
|
public Vector2 OldSize { get; }
|
||||||
|
|
||||||
|
public ResizedEventArgs(Vector2 newSize, Vector2 oldSize)
|
||||||
|
{
|
||||||
|
NewSize = newSize;
|
||||||
|
OldSize = oldSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AdoptedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public UIBase? Parent { get; }
|
||||||
|
|
||||||
|
public AdoptedEventArgs(UIBase? parent = null)
|
||||||
|
{
|
||||||
|
Parent = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|
||||||
@@ -11,4 +11,9 @@
|
|||||||
<EmbeddedResource Include="res/**" />
|
<EmbeddedResource Include="res/**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="OpenTK.Graphics" Version="4.8.2" />
|
||||||
|
<PackageReference Include="OpenTK.Mathematics" Version="4.8.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
133
Dashboard/DbApplication.cs
Normal file
133
Dashboard/DbApplication.cs
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using Dashboard.ImmediateDraw;
|
||||||
|
using Dashboard.Controls;
|
||||||
|
using Dashboard.Media;
|
||||||
|
using Dashboard.PAL;
|
||||||
|
using Dashboard.Typography;
|
||||||
|
|
||||||
|
namespace Dashboard
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Main class for Dashboard applications.
|
||||||
|
/// </summary>
|
||||||
|
public class DbApplication
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The application platform driver.
|
||||||
|
/// </summary>
|
||||||
|
public IDbPlatform Platform { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Title of the application.
|
||||||
|
/// </summary>
|
||||||
|
public string? Title
|
||||||
|
{
|
||||||
|
get => Platform.Title;
|
||||||
|
set => Platform.Title = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Application icon.
|
||||||
|
/// </summary>
|
||||||
|
public Image? Icon
|
||||||
|
{
|
||||||
|
get => Platform.Icon;
|
||||||
|
set => Platform.Icon = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PAL.Dash? MainPort { get; private set; } = null;
|
||||||
|
|
||||||
|
public FontProvider FontProvider { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of media loaders, drivers that load media such as images and fonts.
|
||||||
|
/// </summary>
|
||||||
|
public List<MediaLoader> MediaLoaders { get; } = new List<MediaLoader>();
|
||||||
|
|
||||||
|
public DbApplication(IDbPlatform platform)
|
||||||
|
{
|
||||||
|
Platform = platform;
|
||||||
|
FontProvider = new FontProvider(this);
|
||||||
|
Current = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDisposable? GetMedia(object key, MediaHint hint)
|
||||||
|
{
|
||||||
|
IDisposable? disposable = null;
|
||||||
|
|
||||||
|
foreach (MediaLoader loader in MediaLoaders)
|
||||||
|
{
|
||||||
|
disposable = loader.GetMedia(key, hint);
|
||||||
|
|
||||||
|
if (disposable != null)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return disposable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDisposable? GetMedia<T>(T key, MediaHint hint)
|
||||||
|
{
|
||||||
|
IDisposable? disposable = null;
|
||||||
|
|
||||||
|
foreach (MediaLoader loader in MediaLoaders)
|
||||||
|
{
|
||||||
|
if (loader is MediaLoader<T> typedLoader)
|
||||||
|
{
|
||||||
|
disposable = typedLoader.GetMedia(key, hint);
|
||||||
|
|
||||||
|
if (disposable != null)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return disposable;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DrawList cmd = new DrawList();
|
||||||
|
|
||||||
|
public void Run(View mainView, bool yield = true)
|
||||||
|
{
|
||||||
|
Init(mainView);
|
||||||
|
|
||||||
|
while (RunSync())
|
||||||
|
{
|
||||||
|
if (yield)
|
||||||
|
{
|
||||||
|
Thread.Yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Init(View mainView)
|
||||||
|
{
|
||||||
|
MainPort = new PAL.Dash(Platform) { UIElement = mainView };
|
||||||
|
MainPort.EventRaised += (sender, ea) => mainView.NotifyEvent(sender, ea);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RunSync()
|
||||||
|
{
|
||||||
|
if (!MainPort!.IsValid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Platform.ProcessEvents(false);
|
||||||
|
|
||||||
|
if (MainPort.IsValid)
|
||||||
|
{
|
||||||
|
cmd.Clear();
|
||||||
|
MainPort.Paint(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DbApplication Current { get; private set; } = null!;
|
||||||
|
|
||||||
|
public static void SetCurrentApplication(DbApplication application)
|
||||||
|
{
|
||||||
|
Current = application;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
268
Dashboard/Geometry.cs
Normal file
268
Dashboard/Geometry.cs
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
using OpenTK.Mathematics;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Dashboard
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A bezier curve segment.
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("{Start} -- {ControlA} -- {ControlB} -- {End}")]
|
||||||
|
public struct Bezier
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Segment start point.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 Start;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Start point control point.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 ControlA;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// End point control point.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 ControlB;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Segment end point.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 End;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An approximation of the arc length of the bezier curve, for calculating rasterization resolution.
|
||||||
|
/// </summary>
|
||||||
|
public float RasterizationArc =>
|
||||||
|
0.5f * (End - Start).Length +
|
||||||
|
0.5f * ((ControlA - Start).Length + (ControlB - ControlA).Length + (End - ControlB).Length);
|
||||||
|
|
||||||
|
public Bezier(Vector2 start, Vector2 controlA, Vector2 controlB, Vector2 end)
|
||||||
|
{
|
||||||
|
Start = start;
|
||||||
|
ControlA = controlA;
|
||||||
|
ControlB = controlB;
|
||||||
|
End = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bezier(
|
||||||
|
float startX,
|
||||||
|
float startY,
|
||||||
|
float controlAx,
|
||||||
|
float controlAy,
|
||||||
|
float controlBx,
|
||||||
|
float controlBy,
|
||||||
|
float endX,
|
||||||
|
float endY)
|
||||||
|
: this(
|
||||||
|
new Vector2(startX, startY),
|
||||||
|
new Vector2(controlAx, controlAy),
|
||||||
|
new Vector2(controlBx, controlBy),
|
||||||
|
new Vector2(endX, endY))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a point in the curve segment.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="t">Control parameter (between 0 and 1)</param>
|
||||||
|
/// <returns>The point on the curve.</returns>
|
||||||
|
public Vector2 GetBezierPoint(float t)
|
||||||
|
{
|
||||||
|
float T = 1 - t;
|
||||||
|
return
|
||||||
|
T * T * T * Start +
|
||||||
|
3 * T * T * t * ControlA +
|
||||||
|
3 * T * t * t * ControlB +
|
||||||
|
t * t * t * End;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the tangent on the curve.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="t">Control parameter (between 0 and 1)</param>
|
||||||
|
/// <returns>The tangent curve.</returns>
|
||||||
|
public Vector2 GetBezierTangent(float t)
|
||||||
|
{
|
||||||
|
float T = 1 - t;
|
||||||
|
return
|
||||||
|
(
|
||||||
|
3 * T * T * (ControlA - Start) +
|
||||||
|
6 * T * t * (ControlB - ControlA) +
|
||||||
|
3 * t * t * (End - ControlB)
|
||||||
|
).Normalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Vector2 GetBezierNormal(float t)
|
||||||
|
{
|
||||||
|
Vector2 tangent = GetBezierTangent(t);
|
||||||
|
return new Vector2(-tangent.Y, tangent.X);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A line segment.
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("{Start} -- {End}")]
|
||||||
|
public struct Line
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Start point.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 Start;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// End point.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 End;
|
||||||
|
|
||||||
|
public Line(Vector2 start, Vector2 end)
|
||||||
|
{
|
||||||
|
Start = start;
|
||||||
|
End = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Line(float startX, float startY, float endX, float endY)
|
||||||
|
{
|
||||||
|
Start.X = startX;
|
||||||
|
Start.Y = startY;
|
||||||
|
End.X = endX;
|
||||||
|
End.Y = endY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2 Normal()
|
||||||
|
{
|
||||||
|
Vector2 tangent = Tangent();
|
||||||
|
return new Vector2(-tangent.Y, tangent.X);
|
||||||
|
}
|
||||||
|
public Vector2 Tangent()
|
||||||
|
{
|
||||||
|
return (End - Start).Normalized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A rectangle.
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("({Left}, {Top}, {Right}, {Bottom})")]
|
||||||
|
public struct Rectangle
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Position maximum point.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 Max;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Position minimum point.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 Min;
|
||||||
|
|
||||||
|
public float Left
|
||||||
|
{
|
||||||
|
get => Min.X;
|
||||||
|
set => Min.X = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Right
|
||||||
|
{
|
||||||
|
get => Max.X;
|
||||||
|
set => Max.X = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Top
|
||||||
|
{
|
||||||
|
get => Min.Y;
|
||||||
|
set => Min.Y = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Bottom
|
||||||
|
{
|
||||||
|
get => Max.Y;
|
||||||
|
set => Max.Y = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2 Size
|
||||||
|
{
|
||||||
|
get => Max - Min;
|
||||||
|
set => Max = Min + value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rectangle(Vector2 max, Vector2 min)
|
||||||
|
{
|
||||||
|
Max = max;
|
||||||
|
Min = min;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rectangle(float r, float b, float l, float t)
|
||||||
|
{
|
||||||
|
Max = new Vector2(r, b);
|
||||||
|
Min = new Vector2(l, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(Vector2 point)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
point.X > Left && point.X < Right &&
|
||||||
|
point.Y > Bottom && point.Y < Top;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Translate(in Vector2 offset)
|
||||||
|
{
|
||||||
|
Min += offset;
|
||||||
|
Max += offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Rectangle Intersect(in Rectangle a, in Rectangle b) =>
|
||||||
|
new Rectangle(
|
||||||
|
Math.Max(a.Right, b.Right),
|
||||||
|
Math.Max(a.Bottom, b.Bottom)
|
||||||
|
,
|
||||||
|
Math.Min(a.Left, b.Left),
|
||||||
|
Math.Min(a.Top, b.Top));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An ellipse.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>It is undefined to have an ellipse with non-orthogonal axes.</remarks>
|
||||||
|
[DebuggerDisplay("{Center} ellipse {AxisA}; {AxisB}")]
|
||||||
|
public struct Ellipse
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ellipse center point.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 Center;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// First ellipse axis.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 AxisA;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Second ellipse axis.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 AxisB;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A triangle.
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("{A} -- {B} -- {C}")]
|
||||||
|
public struct Triangle
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// First vertex.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 A;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Second vertex.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 B;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Third vertex.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 C;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Dashboard/IDbAttribute.cs
Normal file
21
Dashboard/IDbAttribute.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Dashboard
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Common interface for Dashboard objects that accept attributes.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Dashboard will call dispose on any and all objects which implement IDisposable.
|
||||||
|
/// If this is an issue, please guard your object against this using a wrapper.
|
||||||
|
/// </remarks
|
||||||
|
public interface IDbAttribute : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Attribute dictionary.
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, object> Attributes { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Quik.CommandMachine
|
namespace Dashboard.ImmediateDraw
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enumeration of built-in Quik commands.
|
/// Enumeration of built-in Quik commands.
|
||||||
8
Dashboard/ImmediateDraw/CommandHandler.cs
Normal file
8
Dashboard/ImmediateDraw/CommandHandler.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Dashboard.ImmediateDraw
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A delegate for a Dashboard engine command.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stack">The current stack.</param>
|
||||||
|
public delegate void CommandHandler(DrawingEngine state, DrawQueue queue);
|
||||||
|
}
|
||||||
233
Dashboard/ImmediateDraw/DrawEngine.cs
Normal file
233
Dashboard/ImmediateDraw/DrawEngine.cs
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Dashboard.ImmediateDraw
|
||||||
|
{
|
||||||
|
public class DrawingEngine
|
||||||
|
{
|
||||||
|
private int _zIndex = 0;
|
||||||
|
private readonly Stack<int> _zStack = new Stack<int>();
|
||||||
|
public int ZIndex => _zIndex;
|
||||||
|
|
||||||
|
private Rectangle _viewport;
|
||||||
|
private readonly Stack<Rectangle> _viewportStack = new Stack<Rectangle>();
|
||||||
|
private readonly Stack<Matrix4> _matrixStack = new Stack<Matrix4>();
|
||||||
|
|
||||||
|
private Command _customCommandBase = Command.CustomCommandBase;
|
||||||
|
private readonly List<CommandHandler> _customCommands = new List<CommandHandler>();
|
||||||
|
|
||||||
|
public Rectangle Viewport => _viewport;
|
||||||
|
|
||||||
|
public Matrix4 ActiveTransforms { get; }
|
||||||
|
|
||||||
|
public StyleStack Style { get; } = new StyleStack(new Style());
|
||||||
|
|
||||||
|
protected DrawingEngine()
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Command RegisterCustomCommand(CommandHandler handler)
|
||||||
|
{
|
||||||
|
Command id = _customCommandBase++;
|
||||||
|
_customCommands.Insert(id - Command.CustomCommandBase, handler);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ProcessCommands(Rectangle bounds, DrawList queue)
|
||||||
|
{
|
||||||
|
DrawQueue iterator = queue.GetEnumerator();
|
||||||
|
|
||||||
|
if (!iterator.Peek().IsCommand)
|
||||||
|
throw new ArgumentException("The first element in the iterator must be a command frame.");
|
||||||
|
|
||||||
|
Reset();
|
||||||
|
|
||||||
|
_viewport = bounds;
|
||||||
|
_viewportStack.Push(_viewport);
|
||||||
|
|
||||||
|
Frame frame;
|
||||||
|
while (iterator.TryDequeue(out frame))
|
||||||
|
{
|
||||||
|
Command cmd = (Command)frame;
|
||||||
|
switch (cmd)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
if (cmd > Command.CustomCommandBase)
|
||||||
|
{
|
||||||
|
_customCommands[cmd - Command.CustomCommandBase].Invoke(this, iterator);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ChildProcessCommand(cmd, iterator);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Command.ConditionalBegin: ConditionalHandler(iterator); break;
|
||||||
|
case Command.ConditionalEnd: /* nop */ break;
|
||||||
|
|
||||||
|
case Command.Invoke:
|
||||||
|
iterator.Dequeue().As<CommandHandler>().Invoke(this, iterator);
|
||||||
|
break;
|
||||||
|
case Command.PushViewport:
|
||||||
|
_viewportStack.Push(_viewport);
|
||||||
|
break;
|
||||||
|
case Command.IntersectViewport:
|
||||||
|
_viewport = Rectangle.Intersect((Rectangle)iterator.Dequeue(), _viewport);
|
||||||
|
break;
|
||||||
|
case Command.StoreViewport:
|
||||||
|
_viewport = (Rectangle)iterator.Dequeue();
|
||||||
|
break;
|
||||||
|
case Command.PopViewport:
|
||||||
|
_viewport = _viewportStack.TryPop(out Rectangle viewport) ? viewport : bounds;
|
||||||
|
break;
|
||||||
|
case Command.PushZ:
|
||||||
|
_zStack.Push(_zIndex);
|
||||||
|
break;
|
||||||
|
case Command.IncrementZ:
|
||||||
|
_zIndex++;
|
||||||
|
break;
|
||||||
|
case Command.AddZ:
|
||||||
|
_zIndex += (int)iterator.Dequeue();
|
||||||
|
break;
|
||||||
|
case Command.StoreZ:
|
||||||
|
_zIndex = (int)iterator.Dequeue();
|
||||||
|
break;
|
||||||
|
case Command.DecrementZ:
|
||||||
|
_zIndex--;
|
||||||
|
break;
|
||||||
|
case Command.PopZ:
|
||||||
|
_zIndex = _zStack.TryPop(out int zindex) ? zindex : 0;
|
||||||
|
break;
|
||||||
|
case Command.PushStyle:
|
||||||
|
Style.Push(iterator.Dequeue().As<Style>());
|
||||||
|
break;
|
||||||
|
case Command.StoreStyle:
|
||||||
|
Style.Pop();
|
||||||
|
Style.Push(iterator.Dequeue().As<Style>());
|
||||||
|
break;
|
||||||
|
case Command.PopStyle:
|
||||||
|
Style.Pop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void ChildProcessCommand(Command name, DrawQueue queue)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Reset()
|
||||||
|
{
|
||||||
|
_zIndex = 0;
|
||||||
|
_zStack.Clear();
|
||||||
|
|
||||||
|
_viewport = new Rectangle(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue);
|
||||||
|
_viewportStack.Clear();
|
||||||
|
|
||||||
|
_matrixStack.Clear();
|
||||||
|
_matrixStack.Push(Matrix4.Identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConditionalHandler(DrawQueue iterator)
|
||||||
|
{
|
||||||
|
Frame frame = iterator.Dequeue();
|
||||||
|
|
||||||
|
if (
|
||||||
|
frame.IsInteger && (int)frame != 0 ||
|
||||||
|
frame.As<Func<bool>>().Invoke())
|
||||||
|
{
|
||||||
|
// Take this branch.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip this branch.
|
||||||
|
int depth = 1;
|
||||||
|
while (iterator.TryPeek(out frame))
|
||||||
|
{
|
||||||
|
if (!frame.IsCommand)
|
||||||
|
{
|
||||||
|
iterator.Dequeue();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ((Command)frame)
|
||||||
|
{
|
||||||
|
case Command.ConditionalBegin:
|
||||||
|
// Increment conditional depth.
|
||||||
|
depth++;
|
||||||
|
break;
|
||||||
|
case Command.ConditionalEnd:
|
||||||
|
// Decrement condional depth, exit if zero.
|
||||||
|
if (--depth == 0)
|
||||||
|
{
|
||||||
|
iterator.Dequeue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator.Dequeue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly Dictionary<Type, IDrawListSerializer> s_serializers = new Dictionary<Type, IDrawListSerializer>();
|
||||||
|
|
||||||
|
/// <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>(IDrawListSerializer 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>(IDrawListSerializer<T> serializer, bool overwrite = false)
|
||||||
|
=> AddSerializer<T>((IDrawListSerializer)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 IDrawListSerializer<T> GetSerializer<T>(IDrawListSerializable<T>? value)
|
||||||
|
where T : IDrawListSerializable<T>, new()
|
||||||
|
{
|
||||||
|
if (!s_serializers.TryGetValue(typeof(T), out var serializer))
|
||||||
|
{
|
||||||
|
serializer = new DrawListSerializableSerializer<T>();
|
||||||
|
AddSerializer<T>(serializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (IDrawListSerializer<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 IDrawListSerializer<T> GetSerializer<T>(T? value)
|
||||||
|
{
|
||||||
|
return (IDrawListSerializer<T>)s_serializers[typeof(T)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
434
Dashboard/ImmediateDraw/DrawList.cs
Normal file
434
Dashboard/ImmediateDraw/DrawList.cs
Normal file
@@ -0,0 +1,434 @@
|
|||||||
|
using Dashboard.Media;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Dashboard.ImmediateDraw
|
||||||
|
{
|
||||||
|
public class DrawList : IEnumerable<Frame>
|
||||||
|
{
|
||||||
|
private readonly List<Frame> _frames = new List<Frame>();
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_frames.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void Enqueue(in Frame frame)
|
||||||
|
{
|
||||||
|
_frames.Add(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Invoke(CommandHandler handler)
|
||||||
|
{
|
||||||
|
Enqueue(Command.Invoke);
|
||||||
|
Enqueue(new Frame(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConditionalBegin(bool value)
|
||||||
|
{
|
||||||
|
Enqueue(Command.ConditionalBegin);
|
||||||
|
Enqueue((Frame)(value ? 1 : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConditionalBegin(Func<bool> condition)
|
||||||
|
{
|
||||||
|
Enqueue(Command.ConditionalBegin);
|
||||||
|
Enqueue(new Frame(condition));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConditionalEnd()
|
||||||
|
{
|
||||||
|
Enqueue(Command.ConditionalEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PushViewport()
|
||||||
|
{
|
||||||
|
Enqueue(Command.PushViewport);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void IntersectViewport(in Rectangle viewport)
|
||||||
|
{
|
||||||
|
Enqueue(Command.IntersectViewport);
|
||||||
|
Enqueue(viewport);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StoreViewport(in Rectangle viewport)
|
||||||
|
{
|
||||||
|
Enqueue(Command.StoreViewport);
|
||||||
|
Enqueue(viewport);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PopViewport()
|
||||||
|
{
|
||||||
|
Enqueue(Command.PopViewport);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PushZ()
|
||||||
|
{
|
||||||
|
Enqueue(Command.PushZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void IncrementZ()
|
||||||
|
{
|
||||||
|
Enqueue(Command.IncrementZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddZ(int value)
|
||||||
|
{
|
||||||
|
if (value == 1)
|
||||||
|
{
|
||||||
|
IncrementZ();
|
||||||
|
}
|
||||||
|
else if (value == -1)
|
||||||
|
{
|
||||||
|
DecrementZ();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Enqueue(Command.AddZ);
|
||||||
|
Enqueue((Frame)value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StoreZ(int value)
|
||||||
|
{
|
||||||
|
Enqueue(Command.StoreZ);
|
||||||
|
Enqueue((Frame)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DecrementZ()
|
||||||
|
{
|
||||||
|
Enqueue(Command.DecrementZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PopZ()
|
||||||
|
{
|
||||||
|
Enqueue(Command.PopZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PushStyle(Style style)
|
||||||
|
{
|
||||||
|
Enqueue(Command.PushStyle);
|
||||||
|
Enqueue(new Frame(style));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StoreStyle(Style style)
|
||||||
|
{
|
||||||
|
Enqueue(Command.StoreStyle);
|
||||||
|
Enqueue(new Frame(style));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PopStyle()
|
||||||
|
{
|
||||||
|
Enqueue(Command.PopStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Line(in Line line)
|
||||||
|
{
|
||||||
|
Enqueue(Command.Line);
|
||||||
|
Enqueue(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Line(params Line[] lines)
|
||||||
|
{
|
||||||
|
Enqueue(Command.Line);
|
||||||
|
Enqueue((Frame)lines.Length);
|
||||||
|
foreach (Line line in lines)
|
||||||
|
Enqueue(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Bezier(in Bezier bezier)
|
||||||
|
{
|
||||||
|
Frame a, b;
|
||||||
|
Frame.Create(bezier, out a, out b);
|
||||||
|
|
||||||
|
Enqueue(Command.Bezier);
|
||||||
|
Enqueue(a);
|
||||||
|
Enqueue(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Bezier(params Bezier[] beziers)
|
||||||
|
{
|
||||||
|
Frame a, b;
|
||||||
|
|
||||||
|
Enqueue(Command.Bezier);
|
||||||
|
Enqueue((Frame)beziers.Length);
|
||||||
|
|
||||||
|
foreach (Bezier bezier in beziers)
|
||||||
|
{
|
||||||
|
Frame.Create(bezier, out a, out b);
|
||||||
|
Enqueue(a);
|
||||||
|
Enqueue(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Rectangle(in Rectangle rectangle)
|
||||||
|
{
|
||||||
|
Enqueue(Command.Rectangle);
|
||||||
|
Enqueue(rectangle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Rectangle(Rectangle[] rectangles)
|
||||||
|
{
|
||||||
|
Enqueue(Command.Rectangle);
|
||||||
|
Enqueue((Frame)rectangles.Length);
|
||||||
|
foreach (Rectangle rectangle in rectangles)
|
||||||
|
Enqueue(rectangle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Ellipse(in Ellipse ellipse)
|
||||||
|
{
|
||||||
|
Frame a, b;
|
||||||
|
Frame.Create(ellipse, out a, out b);
|
||||||
|
|
||||||
|
Enqueue(Command.Ellipse);
|
||||||
|
Enqueue(a);
|
||||||
|
Enqueue(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Ellipse(params Ellipse[] ellipses)
|
||||||
|
{
|
||||||
|
Frame a, b;
|
||||||
|
Enqueue(Command.Ellipse);
|
||||||
|
Enqueue((Frame)ellipses.Length);
|
||||||
|
foreach (Ellipse ellipse in ellipses)
|
||||||
|
{
|
||||||
|
Frame.Create(ellipse, out a, out b);
|
||||||
|
Enqueue(a);
|
||||||
|
Enqueue(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Triangle(in Triangle triangle)
|
||||||
|
{
|
||||||
|
Enqueue(Command.Triangle);
|
||||||
|
Enqueue(triangle.A);
|
||||||
|
Enqueue(triangle.B);
|
||||||
|
Enqueue(triangle.C);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Triangle(params Triangle[] triangles)
|
||||||
|
{
|
||||||
|
Enqueue(Command.Triangle);
|
||||||
|
Enqueue((Frame)triangles.Length);
|
||||||
|
foreach (Triangle triangle in triangles)
|
||||||
|
{
|
||||||
|
Enqueue(triangle.A);
|
||||||
|
Enqueue(triangle.B);
|
||||||
|
Enqueue(triangle.C);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Polygon(params Vector2[] polygon)
|
||||||
|
{
|
||||||
|
Enqueue(Command.Polygon);
|
||||||
|
Enqueue((Frame)polygon.Length);
|
||||||
|
foreach (Vector2 vertex in polygon)
|
||||||
|
{
|
||||||
|
Enqueue(vertex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Image(Image texture, in Rectangle rectangle)
|
||||||
|
{
|
||||||
|
Enqueue(Command.Image);
|
||||||
|
Enqueue((Frame)(int)ImageCommandFlags.Single);
|
||||||
|
Enqueue(new Frame(texture));
|
||||||
|
Enqueue(rectangle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Image(Image texture, in Rectangle rectangle, in Rectangle uv)
|
||||||
|
{
|
||||||
|
Enqueue(Command.Image);
|
||||||
|
Enqueue((Frame)(int)(ImageCommandFlags.Single | ImageCommandFlags.UVs));
|
||||||
|
Enqueue(new Frame(texture));
|
||||||
|
Enqueue(rectangle);
|
||||||
|
Enqueue(uv);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Image(Image texture, ReadOnlySpan<Rectangle> rectangles, bool interleavedUV = false)
|
||||||
|
{
|
||||||
|
int count = rectangles.Length;
|
||||||
|
ImageCommandFlags flags = ImageCommandFlags.None;
|
||||||
|
|
||||||
|
if (interleavedUV)
|
||||||
|
{
|
||||||
|
count /= 2;
|
||||||
|
flags |= ImageCommandFlags.UVs;
|
||||||
|
}
|
||||||
|
|
||||||
|
Enqueue(Command.Image);
|
||||||
|
Enqueue(new Frame((int)flags, count));
|
||||||
|
Enqueue(new Frame(texture));
|
||||||
|
|
||||||
|
foreach (Rectangle rectangle in rectangles)
|
||||||
|
{
|
||||||
|
Enqueue(rectangle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Image(Image texture, ReadOnlySpan<Rectangle> rectangles, ReadOnlySpan<Rectangle> uvs)
|
||||||
|
{
|
||||||
|
int count = Math.Min(rectangles.Length, uvs.Length);
|
||||||
|
Enqueue(Command.Image);
|
||||||
|
Enqueue(new Frame((int)ImageCommandFlags.UVs, count));
|
||||||
|
Enqueue(new Frame(texture));
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
Enqueue(rectangles[i]);
|
||||||
|
Enqueue(uvs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Image3D(Image texture, in Image3DCall call)
|
||||||
|
{
|
||||||
|
Enqueue(Command.Image);
|
||||||
|
Enqueue(new Frame(ImageCommandFlags.Image3d | ImageCommandFlags.Single));
|
||||||
|
Enqueue(new Frame(texture));
|
||||||
|
Enqueue(call.Rectangle);
|
||||||
|
Enqueue(call.UVs);
|
||||||
|
Enqueue(new Frame(call.Layer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Image3D(Image texture, ReadOnlySpan<Image3DCall> calls)
|
||||||
|
{
|
||||||
|
Enqueue(Command.Image);
|
||||||
|
Enqueue(new Frame((int)ImageCommandFlags.Image3d, calls.Length));
|
||||||
|
Enqueue(new Frame(texture));
|
||||||
|
|
||||||
|
foreach (Image3DCall call in calls)
|
||||||
|
{
|
||||||
|
Enqueue(call.Rectangle);
|
||||||
|
Enqueue(call.UVs);
|
||||||
|
Enqueue(new Frame(call.Layer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Splice(DrawList list)
|
||||||
|
{
|
||||||
|
foreach (Frame frame in list)
|
||||||
|
{
|
||||||
|
Enqueue(frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Serialize an object into the command list.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the value to serialize.</typeparam>
|
||||||
|
/// <param name="value">What to write into the command list.</param>
|
||||||
|
public void Write<T>(T value)
|
||||||
|
{
|
||||||
|
DrawingEngine.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>(IDrawListSerializable<T> value)
|
||||||
|
where T : IDrawListSerializable<T>, new()
|
||||||
|
{
|
||||||
|
DrawingEngine.GetSerializer(value).Serialize((T)value, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DrawQueue GetEnumerator() => new DrawQueue(_frames);
|
||||||
|
IEnumerator<Frame> IEnumerable<Frame>.GetEnumerator() => GetEnumerator();
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DrawQueue : IEnumerator<Frame>
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyList<Frame> _frames;
|
||||||
|
private int _current;
|
||||||
|
|
||||||
|
public Frame Current => _frames[_current];
|
||||||
|
|
||||||
|
object IEnumerator.Current => Current;
|
||||||
|
|
||||||
|
public DrawQueue(IReadOnlyList<Frame> frames)
|
||||||
|
{
|
||||||
|
_current = -1;
|
||||||
|
_frames = frames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryDequeue([NotNullWhen(true)] out Frame frame)
|
||||||
|
{
|
||||||
|
if (MoveNext())
|
||||||
|
{
|
||||||
|
frame = Current;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
frame = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Frame Dequeue() => TryDequeue(out Frame frame) ? frame : throw new Exception("No more frames left.");
|
||||||
|
|
||||||
|
public bool TryPeek([NotNullWhen(true)] out Frame frame)
|
||||||
|
{
|
||||||
|
if (_current + 1 < _frames.Count)
|
||||||
|
{
|
||||||
|
frame = _frames[_current + 1];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
frame = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Frame Peek() => TryPeek(out Frame frame) ? frame : throw new Exception("No more frames left.");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deserialize an object from the command queue.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of the object to deserialize.</typeparam>
|
||||||
|
/// <param name="value">The deserialized value.</param>
|
||||||
|
public void Read<T>([NotNull] out T? value)
|
||||||
|
{
|
||||||
|
value = DrawingEngine.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 IDrawListSerializable<T>? value)
|
||||||
|
where T : IDrawListSerializable<T>, new()
|
||||||
|
{
|
||||||
|
value = DrawingEngine.GetSerializer(value = null).Deserialize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (_current + 1 < _frames.Count)
|
||||||
|
{
|
||||||
|
_current++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_current = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
362
Dashboard/ImmediateDraw/Frame.cs
Normal file
362
Dashboard/ImmediateDraw/Frame.cs
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Dashboard.ImmediateDraw
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
public struct Frame
|
||||||
|
{
|
||||||
|
[FieldOffset(0)]
|
||||||
|
private FrameType _type;
|
||||||
|
|
||||||
|
[FieldOffset(sizeof(FrameType) + 0 * sizeof(int))]
|
||||||
|
private int _i1;
|
||||||
|
[FieldOffset(sizeof(FrameType) + 1 * sizeof(int))]
|
||||||
|
private int _i2;
|
||||||
|
[FieldOffset(sizeof(FrameType) + 2 * sizeof(int))]
|
||||||
|
private int _i3;
|
||||||
|
[FieldOffset(sizeof(FrameType) + 3 * sizeof(int))]
|
||||||
|
private int _i4;
|
||||||
|
|
||||||
|
[FieldOffset(sizeof(FrameType) + 0 * sizeof(float))]
|
||||||
|
private float _f1;
|
||||||
|
[FieldOffset(sizeof(FrameType) + 1 * sizeof(float))]
|
||||||
|
private float _f2;
|
||||||
|
[FieldOffset(sizeof(FrameType) + 2 * sizeof(float))]
|
||||||
|
private float _f3;
|
||||||
|
[FieldOffset(sizeof(FrameType) + 3 * sizeof(float))]
|
||||||
|
private float _f4;
|
||||||
|
|
||||||
|
[FieldOffset(24)]
|
||||||
|
private object? _object = null;
|
||||||
|
|
||||||
|
public bool IsCommand => _type == FrameType.Command;
|
||||||
|
public bool IsInteger =>
|
||||||
|
_type == FrameType.IVec1 ||
|
||||||
|
_type == FrameType.IVec2 ||
|
||||||
|
_type == FrameType.IVec3 ||
|
||||||
|
_type == FrameType.IVec4;
|
||||||
|
public bool IsFloat =>
|
||||||
|
_type == FrameType.Vec1 ||
|
||||||
|
_type == FrameType.Vec2 ||
|
||||||
|
_type == FrameType.Vec3 ||
|
||||||
|
_type == FrameType.Vec4;
|
||||||
|
|
||||||
|
public int VectorSize
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
switch (_type)
|
||||||
|
{
|
||||||
|
case FrameType.None:
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
case FrameType.Vec2: case FrameType.IVec2:
|
||||||
|
return 2;
|
||||||
|
case FrameType.Vec3: case FrameType.IVec3:
|
||||||
|
return 3;
|
||||||
|
case FrameType.Vec4: case FrameType.IVec4:
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrameType Type => _type;
|
||||||
|
|
||||||
|
public int I1 => _i1;
|
||||||
|
public int I2 => _i2;
|
||||||
|
public int I3 => _i3;
|
||||||
|
public int I4 => _i4;
|
||||||
|
|
||||||
|
public float F1 => _f1;
|
||||||
|
public float F2 => _f2;
|
||||||
|
public float F3 => _f3;
|
||||||
|
public float F4 => _f4;
|
||||||
|
|
||||||
|
public static Frame None { get; } = new Frame() {
|
||||||
|
_type = FrameType.None
|
||||||
|
};
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
public Frame(Command command) : this()
|
||||||
|
{
|
||||||
|
_type = FrameType.Command;
|
||||||
|
_i1 = (int)command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Frame(object o)
|
||||||
|
{
|
||||||
|
_type = FrameType.Object;
|
||||||
|
|
||||||
|
_i1 = _i2 = _i3 = _i4 = default;
|
||||||
|
_f1 = _f2 = _f3 = _f4 = default;
|
||||||
|
_object = null;
|
||||||
|
|
||||||
|
_object = o;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Frame(int i1)
|
||||||
|
{
|
||||||
|
_type = FrameType.IVec1;
|
||||||
|
|
||||||
|
_i1 = _i2 = _i3 = _i4 = default;
|
||||||
|
_f1 = _f2 = _f3 = _f4 = default;
|
||||||
|
_object = null;
|
||||||
|
|
||||||
|
_i1 = i1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Frame(int i1, int i2)
|
||||||
|
{
|
||||||
|
_type = FrameType.IVec2;
|
||||||
|
|
||||||
|
_i1 = _i2 = _i3 = _i4 = default;
|
||||||
|
_f1 = _f2 = _f3 = _f4 = default;
|
||||||
|
_object = null;
|
||||||
|
|
||||||
|
_i1 = i1;
|
||||||
|
_i2 = i2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Frame(int i1, int i2, int i3)
|
||||||
|
{
|
||||||
|
_type = FrameType.IVec3;
|
||||||
|
|
||||||
|
_i1 = _i2 = _i3 = _i4 = default;
|
||||||
|
_f1 = _f2 = _f3 = _f4 = default;
|
||||||
|
_object = null;
|
||||||
|
|
||||||
|
_i1 = i1;
|
||||||
|
_i2 = i2;
|
||||||
|
_i3 = i3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Frame(int i1, int i2, int i3, int i4)
|
||||||
|
{
|
||||||
|
_type = FrameType.IVec4;
|
||||||
|
|
||||||
|
_i1 = _i2 = _i3 = _i4 = default;
|
||||||
|
_f1 = _f2 = _f3 = _f4 = default;
|
||||||
|
_object = null;
|
||||||
|
|
||||||
|
_i1 = i1;
|
||||||
|
_i2 = i2;
|
||||||
|
_i3 = i3;
|
||||||
|
_i4 = i4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Frame(float f1)
|
||||||
|
{
|
||||||
|
_type = FrameType.Vec1;
|
||||||
|
|
||||||
|
_i1 = _i2 = _i3 = _i4 = default;
|
||||||
|
_f1 = _f2 = _f3 = _f4 = default;
|
||||||
|
_object = null;
|
||||||
|
|
||||||
|
_f1 = f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Frame(float f1, float f2)
|
||||||
|
{
|
||||||
|
_type = FrameType.Vec2;
|
||||||
|
|
||||||
|
_i1 = _i2 = _i3 = _i4 = default;
|
||||||
|
_f1 = _f2 = _f3 = _f4 = default;
|
||||||
|
_object = null;
|
||||||
|
|
||||||
|
_f1 = f1;
|
||||||
|
_f2 = f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Frame(float f1, float f2, float f3)
|
||||||
|
{
|
||||||
|
_type = FrameType.Vec3;
|
||||||
|
|
||||||
|
_i1 = _i2 = _i3 = _i4 = default;
|
||||||
|
_f1 = _f2 = _f3 = _f4 = default;
|
||||||
|
_object = null;
|
||||||
|
|
||||||
|
_f1 = f1;
|
||||||
|
_f2 = f2;
|
||||||
|
_f3 = f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Frame(float f1, float f2, float f3, float f4)
|
||||||
|
{
|
||||||
|
_type = FrameType.Vec4;
|
||||||
|
|
||||||
|
_i1 = _i2 = _i3 = _i4 = default;
|
||||||
|
_f1 = _f2 = _f3 = _f4 = default;
|
||||||
|
_object = null;
|
||||||
|
|
||||||
|
_f1 = f1;
|
||||||
|
_f2 = f2;
|
||||||
|
_f3 = f3;
|
||||||
|
_f4 = f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public T As<T>()
|
||||||
|
{
|
||||||
|
return (T)_object!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetF(int i)
|
||||||
|
{
|
||||||
|
switch (i)
|
||||||
|
{
|
||||||
|
case 0: return _f1;
|
||||||
|
case 1: return _f2;
|
||||||
|
case 2: return _f3;
|
||||||
|
case 3: return _f4;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetI(int i)
|
||||||
|
{
|
||||||
|
switch (i)
|
||||||
|
{
|
||||||
|
case 0: return _i1;
|
||||||
|
case 1: return _i2;
|
||||||
|
case 2: return _i3;
|
||||||
|
case 3: return _i4;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Frame->T Conversion
|
||||||
|
|
||||||
|
public static explicit operator int(in Frame frame)
|
||||||
|
{
|
||||||
|
switch (frame.Type)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
throw new InvalidCastException();
|
||||||
|
case FrameType.Command:
|
||||||
|
case FrameType.IVec1:
|
||||||
|
case FrameType.IVec2:
|
||||||
|
case FrameType.IVec3:
|
||||||
|
case FrameType.IVec4:
|
||||||
|
return frame._i1;
|
||||||
|
case FrameType.Vec1:
|
||||||
|
case FrameType.Vec2:
|
||||||
|
case FrameType.Vec3:
|
||||||
|
case FrameType.Vec4:
|
||||||
|
return (int)frame._f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static explicit operator float(in Frame frame)
|
||||||
|
{
|
||||||
|
switch (frame.Type)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
throw new InvalidCastException();
|
||||||
|
case FrameType.IVec1:
|
||||||
|
case FrameType.IVec2:
|
||||||
|
case FrameType.IVec3:
|
||||||
|
case FrameType.IVec4:
|
||||||
|
return frame._i1;
|
||||||
|
case FrameType.Vec1:
|
||||||
|
case FrameType.Vec2:
|
||||||
|
case FrameType.Vec3:
|
||||||
|
case FrameType.Vec4:
|
||||||
|
return frame._f1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static explicit operator Command(in Frame frame)
|
||||||
|
{
|
||||||
|
if (frame.Type != FrameType.Command)
|
||||||
|
{
|
||||||
|
throw new InvalidCastException("Not a command frame.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Command)frame._i1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static explicit operator Vector2(in Frame frame)
|
||||||
|
{
|
||||||
|
switch (frame.Type)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
throw new InvalidCastException();
|
||||||
|
case FrameType.IVec2:
|
||||||
|
case FrameType.IVec3:
|
||||||
|
case FrameType.IVec4:
|
||||||
|
return new Vector2(frame._i1, frame._i2);
|
||||||
|
case FrameType.Vec2:
|
||||||
|
case FrameType.Vec3:
|
||||||
|
case FrameType.Vec4:
|
||||||
|
return new Vector2(frame._f1, frame._f2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static explicit operator Color4(in Frame frame)
|
||||||
|
{
|
||||||
|
switch (frame.Type)
|
||||||
|
{
|
||||||
|
case FrameType.IVec4:
|
||||||
|
return new Color4((byte)frame._i1, (byte)frame._i2, (byte)frame._i3, (byte)frame._i4);
|
||||||
|
case FrameType.Vec4:
|
||||||
|
return new Color4(frame._f1, frame._f2, frame._f3, frame._f4);
|
||||||
|
default:
|
||||||
|
throw new InvalidCastException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static explicit operator Rectangle(in Frame frame)
|
||||||
|
{
|
||||||
|
switch (frame.Type)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
throw new InvalidCastException();
|
||||||
|
case FrameType.IVec4:
|
||||||
|
return new Rectangle(frame._i1, frame._i2, frame._i3, frame._i4);
|
||||||
|
case FrameType.Vec4:
|
||||||
|
return new Rectangle(frame._f1, frame._f2, frame._f3, frame._f4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static explicit operator Line(in Frame frame)
|
||||||
|
{
|
||||||
|
switch (frame.Type)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
throw new InvalidCastException();
|
||||||
|
case FrameType.IVec4:
|
||||||
|
return new Line(frame._i1, frame._i2, frame._i3, frame._i4);
|
||||||
|
case FrameType.Vec4:
|
||||||
|
return new Line(frame._f1, frame._f2, frame._f3, frame._f4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public static explicit operator Frame(int i) => new Frame(i);
|
||||||
|
public static explicit operator Frame(float f) => new Frame(f);
|
||||||
|
public static implicit operator Frame(Command cmd) => new Frame(cmd);
|
||||||
|
public static implicit operator Frame(in Vector2 vector) => new Frame(vector.X, vector.Y);
|
||||||
|
public static implicit operator Frame(in Color4 color) => new Frame(color.R, color.G, color.B, color.A);
|
||||||
|
public static implicit operator Frame(in Rectangle rect) => new Frame(rect.Max.X, rect.Max.Y, rect.Min.X, rect.Min.Y);
|
||||||
|
public static implicit operator Frame(in Line line) => new Frame(line.Start.X, line.Start.Y, line.End.X, line.Start.Y);
|
||||||
|
|
||||||
|
public static void Create(in Bezier bezier, out Frame a, out Frame b)
|
||||||
|
{
|
||||||
|
a = new Frame(bezier.Start.X, bezier.Start.Y, bezier.End.X, bezier.End.Y);
|
||||||
|
b = new Frame(bezier.ControlA.X, bezier.ControlA.Y, bezier.ControlB.X, bezier.ControlB.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Create(in Ellipse ellipse, out Frame a, out Frame b)
|
||||||
|
{
|
||||||
|
a = new Frame(ellipse.Center.X, ellipse.Center.Y);
|
||||||
|
b = new Frame(ellipse.AxisA.X, ellipse.AxisA.Y, ellipse.AxisB.X, ellipse.AxisB.Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
68
Dashboard/ImmediateDraw/FrameType.cs
Normal file
68
Dashboard/ImmediateDraw/FrameType.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
namespace Dashboard.ImmediateDraw
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Enumeration of command types in the Dashboard command lists.
|
||||||
|
/// </summary>
|
||||||
|
public enum FrameType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A null value.
|
||||||
|
/// </summary>
|
||||||
|
None,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A command frame.
|
||||||
|
/// </summary>
|
||||||
|
Command,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An integer frame.
|
||||||
|
/// </summary>
|
||||||
|
IVec1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A two dimensional integer vector frame.
|
||||||
|
/// </summary>
|
||||||
|
IVec2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A three dimensional integer vector frame.
|
||||||
|
/// </summary>
|
||||||
|
IVec3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A four dimensional integer vector frame.
|
||||||
|
/// </summary>
|
||||||
|
IVec4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A floating point frame.
|
||||||
|
/// </summary>
|
||||||
|
Vec1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A two dimensional floating point vector frame.
|
||||||
|
/// </summary>
|
||||||
|
Vec2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A three dimensional floating point vector frame.
|
||||||
|
/// </summary>
|
||||||
|
Vec3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A four dimensional floating point vector frame.
|
||||||
|
/// </summary>
|
||||||
|
Vec4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A serialized object frame.
|
||||||
|
/// </summary>
|
||||||
|
Serialized,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A .Net object frame.
|
||||||
|
/// </summary>
|
||||||
|
Object,
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Dashboard/ImmediateDraw/Image.cs
Normal file
18
Dashboard/ImmediateDraw/Image.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
namespace Dashboard.ImmediateDraw
|
||||||
|
{
|
||||||
|
public enum ImageCommandFlags
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
Single = 1 << 0,
|
||||||
|
UVs = 1 << 1,
|
||||||
|
Image3d = 1 << 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Image3DCall
|
||||||
|
{
|
||||||
|
public Rectangle Rectangle;
|
||||||
|
public Rectangle UVs;
|
||||||
|
public int Layer;
|
||||||
|
}
|
||||||
|
}
|
||||||
68
Dashboard/ImmediateDraw/Serializers.cs
Normal file
68
Dashboard/ImmediateDraw/Serializers.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace Dashboard.ImmediateDraw
|
||||||
|
{
|
||||||
|
public interface IDrawListSerializable { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interface for objects that can be serialized into the Dashboard command stream.
|
||||||
|
/// </summary>
|
||||||
|
public interface IDrawListSerializable<T> : IDrawListSerializable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Seralize object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="list">The object to serialize into.</param>
|
||||||
|
void Serialize(DrawList list);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deserialize object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="queue">The command queue to deserialize from.</param>
|
||||||
|
void Deserialize(DrawQueue queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base interface for all Command List serializers.
|
||||||
|
/// </summary>
|
||||||
|
public interface IDrawListSerializer { }
|
||||||
|
|
||||||
|
public interface IDrawListSerializer<T> : IDrawListSerializer
|
||||||
|
{
|
||||||
|
/// <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, DrawList 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(DrawQueue queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Class for automatic serialization of <see cref="IDrawListSerializable"/> objects.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The object type to convert.</typeparam>
|
||||||
|
internal class DrawListSerializableSerializer<T> : IDrawListSerializer<T>
|
||||||
|
where T : IDrawListSerializable<T>, new()
|
||||||
|
{
|
||||||
|
public T Deserialize(DrawQueue queue)
|
||||||
|
{
|
||||||
|
T value = new T();
|
||||||
|
value.Deserialize(queue);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Serialize(T value, DrawList list)
|
||||||
|
{
|
||||||
|
value.Serialize(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
116
Dashboard/Layout/GridLayoutAttribute.cs
Normal file
116
Dashboard/Layout/GridLayoutAttribute.cs
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
using OpenTK.Mathematics;
|
||||||
|
using Dashboard.Controls;
|
||||||
|
using System.Reflection.Metadata;
|
||||||
|
|
||||||
|
namespace Dashboard.Layout
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Control attributes for grid layout.
|
||||||
|
/// </summary>
|
||||||
|
public class GridLayoutAttribute
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An anchor will keep the relative distance of a control's edges the same during resizes.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks><see cref="Dock"/> has higher precedence.</remarks>
|
||||||
|
public Anchor Anchor { get; set; } = Anchor.Left | Anchor.Top;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dock will strongly attach a control to its container or its edges.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Has more precedence than <see cref="Anchor"/></remarks>
|
||||||
|
public Dock Dock { get; set; } = Dock.None;
|
||||||
|
|
||||||
|
public void Evaluate(
|
||||||
|
ResizedEventArgs parentResizeEvent,
|
||||||
|
in Rectangle oldBounds,
|
||||||
|
out Rectangle newBounds
|
||||||
|
)
|
||||||
|
{
|
||||||
|
switch (Dock)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case Dock.None:
|
||||||
|
break;
|
||||||
|
case Dock.Top:
|
||||||
|
newBounds = new Rectangle(
|
||||||
|
parentResizeEvent.NewSize.X,
|
||||||
|
oldBounds.Size.Y,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
case Dock.Bottom:
|
||||||
|
newBounds = new Rectangle(
|
||||||
|
parentResizeEvent.NewSize.X,
|
||||||
|
parentResizeEvent.NewSize.Y,
|
||||||
|
0,
|
||||||
|
oldBounds.Size.Y
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
case Dock.Left:
|
||||||
|
newBounds = new Rectangle(
|
||||||
|
oldBounds.Size.X,
|
||||||
|
parentResizeEvent.NewSize.Y,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
case Dock.Right:
|
||||||
|
newBounds = new Rectangle(
|
||||||
|
parentResizeEvent.NewSize.X,
|
||||||
|
parentResizeEvent.NewSize.Y,
|
||||||
|
parentResizeEvent.NewSize.Y - oldBounds.Size.Y,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2 scale = parentResizeEvent.NewSize / parentResizeEvent.OldSize;
|
||||||
|
|
||||||
|
newBounds = oldBounds;
|
||||||
|
|
||||||
|
if (Anchor.HasFlag(Anchor.Top))
|
||||||
|
{
|
||||||
|
newBounds.Top = scale.Y * oldBounds.Top;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Anchor.HasFlag(Anchor.Left))
|
||||||
|
{
|
||||||
|
newBounds.Left = scale.X * oldBounds.Left;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Anchor.HasFlag(Anchor.Bottom))
|
||||||
|
{
|
||||||
|
float margin = scale.Y * (parentResizeEvent.OldSize.Y - newBounds.Bottom);
|
||||||
|
newBounds.Bottom = parentResizeEvent.NewSize.Y - margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Anchor.HasFlag(Anchor.Right))
|
||||||
|
{
|
||||||
|
float margin = scale.X * (parentResizeEvent.OldSize.X - newBounds.Right);
|
||||||
|
newBounds.Right = parentResizeEvent.NewSize.X - margin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the grid layout attribute associated with the given UI-base.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uiBase">The UI-base to query.</param>
|
||||||
|
/// <returns>It's grid layout attribute, if any.</returns>
|
||||||
|
public static GridLayoutAttribute GetGridLayout(UIBase uiBase)
|
||||||
|
{
|
||||||
|
const string GRID_LAYOUT_ATTRIBUTE_KEY = "486ddf8c-b75f-4ad4-a51d-5ba20db9bd0e";
|
||||||
|
|
||||||
|
if (
|
||||||
|
!uiBase.Attributes.TryGetValue(GRID_LAYOUT_ATTRIBUTE_KEY, out object? attribute)
|
||||||
|
|| attribute is not GridLayoutAttribute)
|
||||||
|
{
|
||||||
|
attribute = new GridLayoutAttribute();
|
||||||
|
uiBase.Attributes[GRID_LAYOUT_ATTRIBUTE_KEY] = attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (GridLayoutAttribute)attribute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
namespace Quik.Media.Color
|
namespace Dashboard.Media.Color
|
||||||
{
|
{
|
||||||
public static class FormatConvert
|
public static class FormatConvert
|
||||||
{
|
{
|
||||||
@@ -8,12 +9,12 @@ namespace Quik.Media.Color
|
|||||||
{
|
{
|
||||||
switch (image.Format)
|
switch (image.Format)
|
||||||
{
|
{
|
||||||
case QImageFormat.RaF:
|
case ImageFormat.RaF:
|
||||||
case QImageFormat.RaU8:
|
case ImageFormat.RaU8:
|
||||||
case QImageFormat.RgbF:
|
case ImageFormat.RgbF:
|
||||||
case QImageFormat.RgbU8:
|
case ImageFormat.RgbU8:
|
||||||
case QImageFormat.RgbaF:
|
case ImageFormat.RgbaF:
|
||||||
case QImageFormat.RgbaU8:
|
case ImageFormat.RgbaU8:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
@@ -27,7 +28,7 @@ namespace Quik.Media.Color
|
|||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
QColorF color = io[i];
|
Color4 color = io[i];
|
||||||
|
|
||||||
color.R *= color.A;
|
color.R *= color.A;
|
||||||
color.G *= color.A;
|
color.G *= color.A;
|
||||||
@@ -42,8 +43,8 @@ namespace Quik.Media.Color
|
|||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
QColor color = io[i];
|
Color4 color = io[i];
|
||||||
float a = color.A/255.0f;
|
float a = color.A;
|
||||||
|
|
||||||
color.R = (byte)(color.R * a);
|
color.R = (byte)(color.R * a);
|
||||||
color.G = (byte)(color.G * a);
|
color.G = (byte)(color.G * a);
|
||||||
@@ -75,7 +76,7 @@ namespace Quik.Media.Color
|
|||||||
int count = dst.Width * dst.Height * dst.Depth;
|
int count = dst.Width * dst.Height * dst.Depth;
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
dstIO[i] = (QColor)srcIO[i];
|
dstIO[i] = srcIO[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (dst.Format.IsFloat() && src.Format.IsU8())
|
else if (dst.Format.IsFloat() && src.Format.IsU8())
|
||||||
@@ -86,7 +87,7 @@ namespace Quik.Media.Color
|
|||||||
int count = dst.Width * dst.Height * dst.Depth;
|
int count = dst.Width * dst.Height * dst.Depth;
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
dstIO[i] = (QColorF)srcIO[i];
|
dstIO[i] = srcIO[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (dst.Format.IsFloat() && src.Format.IsFloat())
|
else if (dst.Format.IsFloat() && src.Format.IsFloat())
|
||||||
69
Dashboard/Media/Color/ImageBuffer.cs
Normal file
69
Dashboard/Media/Color/ImageBuffer.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Dashboard.Media.Color
|
||||||
|
{
|
||||||
|
public class QImageBuffer : Image
|
||||||
|
{
|
||||||
|
private byte[] buffer;
|
||||||
|
GCHandle handle;
|
||||||
|
private bool isSdf = false;
|
||||||
|
|
||||||
|
public override ImageFormat InternalFormat { get; }
|
||||||
|
public override int Width { get; }
|
||||||
|
public override int Height { get; }
|
||||||
|
public override int Depth { get; }
|
||||||
|
public override bool IsSdf => isSdf;
|
||||||
|
|
||||||
|
public QImageBuffer(ImageFormat format, int width, int height, int depth = 1)
|
||||||
|
{
|
||||||
|
InternalFormat = format;
|
||||||
|
Width = width;
|
||||||
|
Height = height;
|
||||||
|
Depth = depth;
|
||||||
|
|
||||||
|
buffer = new byte[(long)width * height * depth * format.BytesPerPixel()];
|
||||||
|
}
|
||||||
|
~QImageBuffer()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private QImageLock Lock()
|
||||||
|
{
|
||||||
|
if (handle.IsAllocated) handle.Free();
|
||||||
|
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||||
|
IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
|
||||||
|
return new QImageLock(InternalFormat, Width, Height, Depth, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (handle.IsAllocated) handle.Free();
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void LockBits2d(out QImageLock imageLock, QImageLockOptions options)
|
||||||
|
{
|
||||||
|
imageLock = Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void LockBits3d(out QImageLock imageLock, QImageLockOptions options)
|
||||||
|
{
|
||||||
|
imageLock = Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void LockBits3d(out QImageLock imageLock, QImageLockOptions options, int depth)
|
||||||
|
{
|
||||||
|
imageLock = Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UnlockBits()
|
||||||
|
{
|
||||||
|
handle.Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSdf(bool value = true) => isSdf = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
namespace Quik.Media.Color
|
namespace Dashboard.Media.Color
|
||||||
{
|
{
|
||||||
public unsafe struct LockIO
|
public unsafe struct LockIO
|
||||||
{
|
{
|
||||||
@@ -8,7 +9,7 @@ namespace Quik.Media.Color
|
|||||||
public int Width => Lock.Width;
|
public int Width => Lock.Width;
|
||||||
public int Height => Lock.Height;
|
public int Height => Lock.Height;
|
||||||
public int Depth => Depth;
|
public int Depth => Depth;
|
||||||
public QImageFormat Format => Lock.Format;
|
public ImageFormat Format => Lock.Format;
|
||||||
|
|
||||||
public LockIO(QImageLock imageLock)
|
public LockIO(QImageLock imageLock)
|
||||||
{
|
{
|
||||||
@@ -18,7 +19,7 @@ namespace Quik.Media.Color
|
|||||||
Lock = imageLock;
|
Lock = imageLock;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QColor this[int index]
|
public Color4 this[int index]
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@@ -28,11 +29,11 @@ namespace Quik.Media.Color
|
|||||||
switch (Format)
|
switch (Format)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case QImageFormat.RedU8: return new QColor(ptr[0], 0, 0, 255);
|
case ImageFormat.RedU8: return new Color4(ptr[0], 0, 0, 255);
|
||||||
case QImageFormat.AlphaU8: return new QColor(0, 0, 0, ptr[0]);
|
case ImageFormat.AlphaU8: return new Color4(0, 0, 0, ptr[0]);
|
||||||
case QImageFormat.RaU8: return new QColor(ptr[0], 0, 0, ptr[1]);
|
case ImageFormat.RaU8: return new Color4(ptr[0], 0, 0, ptr[1]);
|
||||||
case QImageFormat.RgbU8: return new QColor(ptr[0], ptr[1], ptr[2], 255);
|
case ImageFormat.RgbU8: return new Color4(ptr[0], ptr[1], ptr[2], 255);
|
||||||
case QImageFormat.RgbaU8: return new QColor(ptr[0], ptr[1], ptr[2], ptr[3]);
|
case ImageFormat.RgbaU8: return new Color4(ptr[0], ptr[1], ptr[2], ptr[3]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,31 +45,31 @@ namespace Quik.Media.Color
|
|||||||
switch (Format)
|
switch (Format)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case QImageFormat.RedU8:
|
case ImageFormat.RedU8:
|
||||||
ptr[0] = value.R;
|
ptr[0] = (byte)(value.R * 255);
|
||||||
break;
|
break;
|
||||||
case QImageFormat.AlphaU8:
|
case ImageFormat.AlphaU8:
|
||||||
ptr[0] = value.A;
|
ptr[0] = (byte)(value.A * 255);
|
||||||
break;
|
break;
|
||||||
case QImageFormat.RaU8:
|
case ImageFormat.RaU8:
|
||||||
ptr[0] = value.R;
|
ptr[0] = (byte)(value.R * 255);
|
||||||
ptr[1] = value.A;
|
ptr[1] = (byte)(value.A * 255);
|
||||||
break;
|
break;
|
||||||
case QImageFormat.RgbU8:
|
case ImageFormat.RgbU8:
|
||||||
ptr[0] = value.R;
|
ptr[0] = (byte)(value.R * 255);
|
||||||
ptr[1] = value.G;
|
ptr[1] = (byte)(value.G * 255);
|
||||||
ptr[2] = value.B;
|
ptr[2] = (byte)(value.B * 255);
|
||||||
break;
|
break;
|
||||||
case QImageFormat.RgbaU8:
|
case ImageFormat.RgbaU8:
|
||||||
ptr[0] = value.R;
|
ptr[0] = (byte)(value.R * 255);
|
||||||
ptr[1] = value.G;
|
ptr[1] = (byte)(value.G * 255);
|
||||||
ptr[2] = value.B;
|
ptr[2] = (byte)(value.B * 255);
|
||||||
ptr[3] = value.A;
|
ptr[3] = (byte)(value.A * 255);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public QColor this[int x, int y, int z = 0]
|
public Color4 this[int x, int y, int z = 0]
|
||||||
{
|
{
|
||||||
get => this[x + y * Width + z * Width * Height];
|
get => this[x + y * Width + z * Width * Height];
|
||||||
set => this[x + y * Width + z * Width * Height] = value;
|
set => this[x + y * Width + z * Width * Height] = value;
|
||||||
@@ -81,7 +82,7 @@ namespace Quik.Media.Color
|
|||||||
public int Width => Lock.Width;
|
public int Width => Lock.Width;
|
||||||
public int Height => Lock.Height;
|
public int Height => Lock.Height;
|
||||||
public int Depth => Depth;
|
public int Depth => Depth;
|
||||||
public QImageFormat Format => Lock.Format;
|
public ImageFormat Format => Lock.Format;
|
||||||
|
|
||||||
public LockIOF(QImageLock imageLock)
|
public LockIOF(QImageLock imageLock)
|
||||||
{
|
{
|
||||||
@@ -91,7 +92,7 @@ namespace Quik.Media.Color
|
|||||||
Lock = imageLock;
|
Lock = imageLock;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QColorF this[int index]
|
public Color4 this[int index]
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@@ -101,11 +102,11 @@ namespace Quik.Media.Color
|
|||||||
switch (Format)
|
switch (Format)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case QImageFormat.RedU8: return new QColorF(ptr[0], 0, 0, 255);
|
case ImageFormat.RedU8: return new Color4(ptr[0], 0, 0, 1);
|
||||||
case QImageFormat.AlphaU8: return new QColorF(0, 0, 0, ptr[0]);
|
case ImageFormat.AlphaU8: return new Color4(0, 0, 0, ptr[0]);
|
||||||
case QImageFormat.RaU8: return new QColorF(ptr[0], 0, 0, ptr[1]);
|
case ImageFormat.RaU8: return new Color4(ptr[0], 0, 0, ptr[1]);
|
||||||
case QImageFormat.RgbU8: return new QColorF(ptr[0], ptr[1], ptr[2], 255);
|
case ImageFormat.RgbU8: return new Color4(ptr[0], ptr[1], ptr[2], 1);
|
||||||
case QImageFormat.RgbaU8: return new QColorF(ptr[0], ptr[1], ptr[2], ptr[3]);
|
case ImageFormat.RgbaU8: return new Color4(ptr[0], ptr[1], ptr[2], ptr[3]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,22 +118,22 @@ namespace Quik.Media.Color
|
|||||||
switch (Format)
|
switch (Format)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case QImageFormat.RedU8:
|
case ImageFormat.RedU8:
|
||||||
ptr[0] = value.R;
|
ptr[0] = value.R;
|
||||||
break;
|
break;
|
||||||
case QImageFormat.AlphaU8:
|
case ImageFormat.AlphaU8:
|
||||||
ptr[0] = value.A;
|
ptr[0] = value.A;
|
||||||
break;
|
break;
|
||||||
case QImageFormat.RaU8:
|
case ImageFormat.RaU8:
|
||||||
ptr[0] = value.R;
|
ptr[0] = value.R;
|
||||||
ptr[1] = value.A;
|
ptr[1] = value.A;
|
||||||
break;
|
break;
|
||||||
case QImageFormat.RgbU8:
|
case ImageFormat.RgbU8:
|
||||||
ptr[0] = value.R;
|
ptr[0] = value.R;
|
||||||
ptr[1] = value.G;
|
ptr[1] = value.G;
|
||||||
ptr[2] = value.B;
|
ptr[2] = value.B;
|
||||||
break;
|
break;
|
||||||
case QImageFormat.RgbaU8:
|
case ImageFormat.RgbaU8:
|
||||||
ptr[0] = value.R;
|
ptr[0] = value.R;
|
||||||
ptr[1] = value.G;
|
ptr[1] = value.G;
|
||||||
ptr[2] = value.B;
|
ptr[2] = value.B;
|
||||||
@@ -141,7 +142,7 @@ namespace Quik.Media.Color
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public QColorF this[int x, int y, int z = 0]
|
public Color4 this[int x, int y, int z = 0]
|
||||||
{
|
{
|
||||||
get => this[x + y * Width + z * Width * Height];
|
get => this[x + y * Width + z * Width * Height];
|
||||||
set => this[x + y * Width + z * Width * Height] = value;
|
set => this[x + y * Width + z * Width * Height] = value;
|
||||||
71
Dashboard/Media/Extensions.cs
Normal file
71
Dashboard/Media/Extensions.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
namespace Dashboard.Media
|
||||||
|
{
|
||||||
|
public static class Extensions
|
||||||
|
{
|
||||||
|
public static bool IsU8(this ImageFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case ImageFormat.AlphaU8:
|
||||||
|
case ImageFormat.RedU8:
|
||||||
|
case ImageFormat.RaU8:
|
||||||
|
case ImageFormat.RgbU8:
|
||||||
|
case ImageFormat.RgbaU8:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsFloat(this ImageFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case ImageFormat.AlphaF:
|
||||||
|
case ImageFormat.RedF:
|
||||||
|
case ImageFormat.RaF:
|
||||||
|
case ImageFormat.RgbF:
|
||||||
|
case ImageFormat.RgbaF:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int BytesPerPixel(this ImageFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case ImageFormat.AlphaU8: return sizeof(byte);
|
||||||
|
case ImageFormat.RedU8: return sizeof(byte);
|
||||||
|
case ImageFormat.RaU8: return 2 * sizeof(byte);
|
||||||
|
case ImageFormat.RgbU8: return 3 * sizeof(byte);
|
||||||
|
case ImageFormat.RgbaU8: return 4 * sizeof(byte);
|
||||||
|
case ImageFormat.AlphaF: return sizeof(float);
|
||||||
|
case ImageFormat.RedF: return sizeof(float);
|
||||||
|
case ImageFormat.RaF: return 2 * sizeof(float);
|
||||||
|
case ImageFormat.RgbF: return 3 * sizeof(float);
|
||||||
|
case ImageFormat.RgbaF: return 4 * sizeof(float);
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Channels(this ImageFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case ImageFormat.AlphaU8: return 1;
|
||||||
|
case ImageFormat.RedU8: return 1;
|
||||||
|
case ImageFormat.RaU8: return 2;
|
||||||
|
case ImageFormat.RgbU8: return 3;
|
||||||
|
case ImageFormat.RgbaU8: return 4;
|
||||||
|
case ImageFormat.AlphaF: return 1;
|
||||||
|
case ImageFormat.RedF: return 1;
|
||||||
|
case ImageFormat.RaF: return 2;
|
||||||
|
case ImageFormat.RgbF: return 3;
|
||||||
|
case ImageFormat.RgbaF: return 4;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
129
Dashboard/Media/Font.cs
Normal file
129
Dashboard/Media/Font.cs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Dashboard.Media;
|
||||||
|
using Dashboard.Media.Fonts;
|
||||||
|
|
||||||
|
namespace Dashboard.Media
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Abstract class that represents a font.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class Font : 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 Image Render(out GlyphMetrics 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;
|
||||||
|
|
||||||
|
DbApplication.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, DbApplication.Current.FontProvider.RasterizerOptions.Sdf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Get(int codepoint, out FontGlyph glyph, Font font)
|
||||||
|
{
|
||||||
|
if (glyphs.TryGetValue(codepoint, out glyph))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Image image = font.Render(
|
||||||
|
out GlyphMetrics metrics,
|
||||||
|
codepoint,
|
||||||
|
Size,
|
||||||
|
DbApplication.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 Image? Image;
|
||||||
|
public readonly GlyphMetrics Metrics;
|
||||||
|
public readonly Rectangle UVs;
|
||||||
|
|
||||||
|
public FontGlyph(int codepoint, Image? image, in GlyphMetrics metrics, in Rectangle 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
185
Dashboard/Media/Font/FontAtlas.cs
Normal file
185
Dashboard/Media/Font/FontAtlas.cs
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Dashboard.Media.Color;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Dashboard.Media.Fonts
|
||||||
|
{
|
||||||
|
public struct FontAtlasGlyphInfo
|
||||||
|
{
|
||||||
|
public int Codepoint;
|
||||||
|
public Image Image;
|
||||||
|
public Rectangle 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 Image 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(ImageFormat.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();
|
||||||
|
|
||||||
|
Vector2 min = new Vector2((float)PointerX/Image.Width, (float)PointerY/Image.Height);
|
||||||
|
Vector2 size = new Vector2((float)src.Width/Image.Width, (float)src.Height/Image.Height);
|
||||||
|
|
||||||
|
prototype.Image = Image;
|
||||||
|
prototype.UVs = new Rectangle(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.Fonts
|
||||||
|
{
|
||||||
|
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.Fonts
|
||||||
|
{
|
||||||
|
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.Fonts
|
||||||
|
{
|
||||||
|
/// <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.Fonts
|
||||||
|
{
|
||||||
|
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.Fonts
|
||||||
|
{
|
||||||
|
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,50 +1,45 @@
|
|||||||
namespace Quik.Media
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Dashboard.Media
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Glyph properties with metrics based on FreeType glyph metrics.
|
/// Glyph properties with metrics based on FreeType glyph metrics.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct QGlyphMetrics
|
public struct GlyphMetrics
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The code point for the character.
|
/// The code point for the character.
|
||||||
/// </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>
|
||||||
public QVec2 Size { get; }
|
public Vector2 Size { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bearing vector for horizontal layout.
|
/// Bearing vector for horizontal layout.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public QVec2 HorizontalBearing { get; }
|
public Vector2 HorizontalBearing { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bearing vector for vertical layout.
|
/// Bearing vector for vertical layout.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public QVec2 VerticalBearing { get; }
|
public Vector2 VerticalBearing { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Advance vector for vertical and horizontal layouts.
|
/// Advance vector for vertical and horizontal layouts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public QVec2 Advance { get; }
|
public Vector2 Advance { get; }
|
||||||
|
|
||||||
public QGlyphMetrics(
|
public GlyphMetrics(
|
||||||
int character,
|
int character,
|
||||||
QRectangle location,
|
Vector2 size,
|
||||||
QVec2 size,
|
Vector2 horizontalBearing,
|
||||||
QVec2 horizontalBearing,
|
Vector2 verticalBearing,
|
||||||
QVec2 verticalBearing,
|
Vector2 advance)
|
||||||
QVec2 advance)
|
|
||||||
{
|
{
|
||||||
Rune = character;
|
Rune = character;
|
||||||
Location = location;
|
|
||||||
Size = size;
|
Size = size;
|
||||||
HorizontalBearing = horizontalBearing;
|
HorizontalBearing = horizontalBearing;
|
||||||
VerticalBearing = verticalBearing;
|
VerticalBearing = verticalBearing;
|
||||||
120
Dashboard/Media/Image.cs
Normal file
120
Dashboard/Media/Image.cs
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
using System;
|
||||||
|
namespace Dashboard.Media
|
||||||
|
{
|
||||||
|
public abstract class Image : IDisposable
|
||||||
|
{
|
||||||
|
public abstract int Width { get; }
|
||||||
|
public abstract int Height { get; }
|
||||||
|
public abstract int Depth { get; }
|
||||||
|
public abstract ImageFormat 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 ImageFormat Format { get; }
|
||||||
|
public bool Premultiply { get; }
|
||||||
|
public int MipLevel { get; }
|
||||||
|
public static QImageLockOptions Default { get; } = new QImageLockOptions(ImageFormat.RgbaU8, true, 0);
|
||||||
|
|
||||||
|
public QImageLockOptions(ImageFormat format, bool premultiply, int level)
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
Premultiply = premultiply;
|
||||||
|
MipLevel = level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct QImageLock
|
||||||
|
{
|
||||||
|
public ImageFormat Format { get; }
|
||||||
|
public int Width { get; }
|
||||||
|
public int Height { get; }
|
||||||
|
public int Depth { get; }
|
||||||
|
public IntPtr ImagePtr { get; }
|
||||||
|
|
||||||
|
public QImageLock(ImageFormat 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.Media
|
namespace Dashboard.Media
|
||||||
{
|
{
|
||||||
public enum QImageFormat
|
public enum ImageFormat
|
||||||
{
|
{
|
||||||
Undefined,
|
Undefined,
|
||||||
RedU8,
|
RedU8,
|
||||||
@@ -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
|
||||||
{
|
{
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using OpenTK.Mathematics;
|
||||||
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,15 +12,15 @@ 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
|
||||||
{
|
{
|
||||||
public readonly QVec2 AbsolutePosition;
|
public readonly Vector2 AbsolutePosition;
|
||||||
public readonly MouseButton ButtonsDown;
|
public readonly MouseButton ButtonsDown;
|
||||||
|
|
||||||
public MouseState(QVec2 position, MouseButton down)
|
public MouseState(Vector2 position, MouseButton down)
|
||||||
{
|
{
|
||||||
AbsolutePosition = position;
|
AbsolutePosition = position;
|
||||||
ButtonsDown = down;
|
ButtonsDown = down;
|
||||||
@@ -28,16 +29,16 @@ namespace Quik
|
|||||||
|
|
||||||
public class MouseButtonEventArgs : EventArgs
|
public class MouseButtonEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public QVec2 AbsolutePosition { get; }
|
public Vector2 AbsolutePosition { get; }
|
||||||
public MouseButton Buttons { get; }
|
public MouseButton Buttons { get; }
|
||||||
|
|
||||||
public MouseButtonEventArgs(QVec2 position, MouseButton buttons)
|
public MouseButtonEventArgs(Vector2 position, MouseButton buttons)
|
||||||
{
|
{
|
||||||
AbsolutePosition = position;
|
AbsolutePosition = position;
|
||||||
Buttons = buttons;
|
Buttons = buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QVec2 RelativePosition(QVec2 origin)
|
public Vector2 RelativePosition(Vector2 origin)
|
||||||
{
|
{
|
||||||
return AbsolutePosition - origin;
|
return AbsolutePosition - origin;
|
||||||
}
|
}
|
||||||
@@ -50,18 +51,18 @@ namespace Quik
|
|||||||
|
|
||||||
public class MouseMoveEventArgs : EventArgs
|
public class MouseMoveEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public QVec2 AbsolutePosition { get; }
|
public Vector2 AbsolutePosition { get; }
|
||||||
public QVec2 LastPosition { get; }
|
public Vector2 LastPosition { get; }
|
||||||
public QVec2 Motion { get; }
|
public Vector2 Motion { get; }
|
||||||
|
|
||||||
public MouseMoveEventArgs(QVec2 position, QVec2 lastPosition)
|
public MouseMoveEventArgs(Vector2 position, Vector2 lastPosition)
|
||||||
{
|
{
|
||||||
AbsolutePosition = position;
|
AbsolutePosition = position;
|
||||||
LastPosition = lastPosition;
|
LastPosition = lastPosition;
|
||||||
Motion = position - lastPosition;
|
Motion = position - lastPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QVec2 RelativePosition(QVec2 origin)
|
public Vector2 RelativePosition(Vector2 origin)
|
||||||
{
|
{
|
||||||
return AbsolutePosition - origin;
|
return AbsolutePosition - origin;
|
||||||
}
|
}
|
||||||
460
Dashboard/OpenGL/GL21Driver.cs
Normal file
460
Dashboard/OpenGL/GL21Driver.cs
Normal file
@@ -0,0 +1,460 @@
|
|||||||
|
using Dashboard.VertexGenerator;
|
||||||
|
using Dashboard.Media;
|
||||||
|
using OpenTK.Graphics.OpenGL4;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Dashboard.OpenGL
|
||||||
|
{
|
||||||
|
public class GL21Driver : IDisposable
|
||||||
|
{
|
||||||
|
private int program;
|
||||||
|
private int v2Position;
|
||||||
|
private int fZIndex;
|
||||||
|
private int v2TexPos;
|
||||||
|
private int fTexLayer;
|
||||||
|
private int v4Color;
|
||||||
|
private int m4Transforms;
|
||||||
|
private int fMaxZ;
|
||||||
|
private int iEnableSdf;
|
||||||
|
private int iEnableTexture;
|
||||||
|
private int iAlphaDiscard;
|
||||||
|
private int fSdfThreshold;
|
||||||
|
private int tx2d;
|
||||||
|
private int tx2darray;
|
||||||
|
|
||||||
|
private bool isDiposed;
|
||||||
|
private readonly Dictionary<DrawCallQueue, DrawData> data = new Dictionary<DrawCallQueue, DrawData>();
|
||||||
|
private readonly TextureManager textures = new TextureManager();
|
||||||
|
|
||||||
|
public bool IsInit { get; private set; } = false;
|
||||||
|
public event Action<GL21Driver>? OnGCDispose;
|
||||||
|
|
||||||
|
public GL21Driver()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~GL21Driver()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Init()
|
||||||
|
{
|
||||||
|
if (IsInit) return;
|
||||||
|
|
||||||
|
int vs = CreateShader(ShaderType.VertexShader, "Dashboard.res.gl21.vert");
|
||||||
|
int fs = CreateShader(ShaderType.FragmentShader, "Dashboard.res.gl21.frag");
|
||||||
|
|
||||||
|
program = GL.CreateProgram();
|
||||||
|
GL.AttachShader(program, vs);
|
||||||
|
GL.AttachShader(program, fs);
|
||||||
|
|
||||||
|
GL.LinkProgram(program);
|
||||||
|
|
||||||
|
if (CheckProgram(program, out string msg) == false)
|
||||||
|
{
|
||||||
|
GraphicsException ex = new GraphicsException("Could not link shader program.");
|
||||||
|
ex.Data.Add("Program Info Log", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
GL.DeleteShader(vs);
|
||||||
|
GL.DeleteShader(fs);
|
||||||
|
|
||||||
|
v2Position = GL.GetAttribLocation(program, nameof(v2Position));
|
||||||
|
fZIndex = GL.GetAttribLocation(program, nameof(fZIndex));
|
||||||
|
v2TexPos = GL.GetAttribLocation(program, nameof(v2TexPos));
|
||||||
|
fTexLayer = GL.GetAttribLocation(program, nameof(fTexLayer));
|
||||||
|
v4Color = GL.GetAttribLocation(program, nameof(v4Color));
|
||||||
|
|
||||||
|
m4Transforms = GL.GetUniformLocation(program, nameof(m4Transforms));
|
||||||
|
fMaxZ = GL.GetUniformLocation(program, nameof(fMaxZ));
|
||||||
|
fSdfThreshold = GL.GetUniformLocation(program, nameof(fSdfThreshold));
|
||||||
|
iEnableSdf = GL.GetUniformLocation(program, nameof(iEnableSdf));
|
||||||
|
iEnableTexture = GL.GetUniformLocation(program, nameof(iEnableTexture));
|
||||||
|
iAlphaDiscard = GL.GetUniformLocation(program, nameof(iAlphaDiscard));
|
||||||
|
tx2d = GL.GetUniformLocation(program, nameof(tx2d));
|
||||||
|
tx2darray = GL.GetUniformLocation(program, nameof(tx2darray));
|
||||||
|
|
||||||
|
IsInit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AssertInit()
|
||||||
|
{
|
||||||
|
if (!IsInit) throw new InvalidOperationException("Initialize the driver first.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw(DrawCallQueue queue, in Rectangle view)
|
||||||
|
{
|
||||||
|
AssertInit();
|
||||||
|
|
||||||
|
if (!data.TryGetValue(queue, out DrawData? draw))
|
||||||
|
{
|
||||||
|
draw = new DrawData(this, queue);
|
||||||
|
data.Add(queue, draw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This already binds the vertex array for me.
|
||||||
|
draw.PrepareFrame();
|
||||||
|
|
||||||
|
|
||||||
|
Vector2 size = view.Size;
|
||||||
|
Matrix4 viewMatrix = Matrix4.CreateOrthographicOffCenter(view.Left, view.Right, view.Bottom, view.Top, 1, -1);
|
||||||
|
|
||||||
|
GL.Viewport(0, 0, (int)view.Size.X, (int)view.Size.Y);
|
||||||
|
GL.UseProgram(program);
|
||||||
|
GL.Uniform1(fMaxZ, (float)(queue.ZDepth+1));
|
||||||
|
GL.Uniform1(fSdfThreshold, 0.5f);
|
||||||
|
GL.Uniform1(tx2d, 0);
|
||||||
|
GL.Enable(EnableCap.Blend);
|
||||||
|
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
|
||||||
|
GL.Enable(EnableCap.ScissorTest);
|
||||||
|
GL.Enable(EnableCap.DepthTest);
|
||||||
|
GL.DepthFunc(DepthFunction.Less);
|
||||||
|
|
||||||
|
foreach (DrawCall call in queue)
|
||||||
|
{
|
||||||
|
GL.Scissor(
|
||||||
|
(int)MathF.Round(call.Bounds.Min.X),
|
||||||
|
(int)MathF.Round(size.Y - call.Bounds.Max.Y),
|
||||||
|
(int)MathF.Round(call.Bounds.Size.X),
|
||||||
|
(int)MathF.Round(call.Bounds.Size.Y));
|
||||||
|
Matrix4 modelMatrix = Matrix4.CreateTranslation(call.Bounds.Min.X, call.Bounds.Min.Y, 0);
|
||||||
|
Matrix4 modelView = viewMatrix * modelMatrix;
|
||||||
|
GL.UniformMatrix4(m4Transforms, false, ref modelView);
|
||||||
|
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture0);
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, 0);
|
||||||
|
if (call.Texture != null)
|
||||||
|
{
|
||||||
|
GL.Uniform1(iEnableSdf, call.Texture.IsSdf ? 1 : 0);
|
||||||
|
GL.Uniform1(iAlphaDiscard, 1);
|
||||||
|
|
||||||
|
if (call.Texture.Depth > 1)
|
||||||
|
{
|
||||||
|
GL.Uniform1(iEnableTexture, 3);
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture1);
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, textures.GetTexture(call.Texture));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GL.Uniform1(iEnableTexture, 2);
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture0);
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, textures.GetTexture(call.Texture));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GL.Uniform1(iEnableTexture, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
GL.DrawElements(PrimitiveType.Triangles, call.Count, DrawElementsType.UnsignedInt, sizeof(int)*call.Start);
|
||||||
|
}
|
||||||
|
|
||||||
|
GL.Disable(EnableCap.ScissorTest);
|
||||||
|
GL.Disable(EnableCap.DepthTest);
|
||||||
|
GL.Disable(EnableCap.Blend);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearDrawQueue(DrawCallQueue queue)
|
||||||
|
{
|
||||||
|
AssertInit();
|
||||||
|
|
||||||
|
if (!data.TryGetValue(queue, out DrawData? draw))
|
||||||
|
return;
|
||||||
|
draw.Dispose();
|
||||||
|
data.Remove(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CreateShader(ShaderType type, string name)
|
||||||
|
{
|
||||||
|
StreamReader source = new StreamReader(typeof(GL21Driver).Assembly.GetManifestResourceStream(name) ?? throw new Exception("Resource not found."));
|
||||||
|
string text = source.ReadToEnd();
|
||||||
|
source.Dispose();
|
||||||
|
|
||||||
|
int shader = GL.CreateShader(type);
|
||||||
|
GL.ShaderSource(shader, text);
|
||||||
|
GL.CompileShader(shader);
|
||||||
|
|
||||||
|
if (CheckShader(shader, out string msg) == false)
|
||||||
|
{
|
||||||
|
GraphicsException ex = new GraphicsException($"Failed to compile {type} shader stage.");
|
||||||
|
ex.Data.Add("Shader Stage", type);
|
||||||
|
ex.Data.Add("Shader Info Log", msg);
|
||||||
|
ex.Data.Add("Shader Source", text);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool CheckShader(int shader, out string message)
|
||||||
|
{
|
||||||
|
message = string.Empty;
|
||||||
|
|
||||||
|
GL.GetShader(shader, ShaderParameter.CompileStatus, out int i);
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
message = GL.GetShaderInfoLog(shader);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool CheckProgram(int program, out string message)
|
||||||
|
{
|
||||||
|
message = string.Empty;
|
||||||
|
|
||||||
|
GL.GetProgram(program, GetProgramParameterName.LinkStatus, out int i);
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
message = GL.GetProgramInfoLog(program);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (isDiposed) return;
|
||||||
|
|
||||||
|
if (!IsInit)
|
||||||
|
{
|
||||||
|
isDiposed = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!disposing)
|
||||||
|
{
|
||||||
|
if (OnGCDispose == null)
|
||||||
|
{
|
||||||
|
throw new Exception("This object must strictly be disposed from the owning thread, not GC");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnGCDispose(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GL.DeleteProgram(program);
|
||||||
|
|
||||||
|
foreach (DrawData datum in data.Values)
|
||||||
|
{
|
||||||
|
datum.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
isDiposed = true;
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => Dispose(true);
|
||||||
|
|
||||||
|
private class DrawData : IDisposable
|
||||||
|
{
|
||||||
|
public DrawCallQueue Queue { get; }
|
||||||
|
public int VertexArray { get; }
|
||||||
|
|
||||||
|
private readonly GL21Driver driver;
|
||||||
|
private int vbo1, vbo2;
|
||||||
|
private int ebo1, ebo2;
|
||||||
|
|
||||||
|
public DrawData(GL21Driver driver, DrawCallQueue queue)
|
||||||
|
{
|
||||||
|
Queue = queue;
|
||||||
|
this.driver = driver;
|
||||||
|
VertexArray = GL.GenVertexArray();
|
||||||
|
GL.GenBuffers(1, out vbo1);
|
||||||
|
GL.GenBuffers(1, out vbo2);
|
||||||
|
GL.GenBuffers(1, out ebo1);
|
||||||
|
GL.GenBuffers(1, out ebo2);
|
||||||
|
isDisposed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PrepareFrame()
|
||||||
|
{
|
||||||
|
int vbo, ebo;
|
||||||
|
vbo = Swap(ref vbo1, ref vbo2);
|
||||||
|
ebo = Swap(ref ebo1, ref ebo2);
|
||||||
|
|
||||||
|
if (Queue.VertexCount == 0 || Queue.ElementCount == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
GL.BindVertexArray(VertexArray);
|
||||||
|
GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
|
||||||
|
GL.BufferData(BufferTarget.ArrayBuffer, DbVertex.Stride * Queue.VertexCount, Queue.VertexArray, BufferUsageHint.StreamDraw);
|
||||||
|
|
||||||
|
GL.VertexAttribPointer(driver.v2Position, 2, VertexAttribPointerType.Float, false, DbVertex.Stride, DbVertex.PositionOffset);
|
||||||
|
GL.VertexAttribPointer(driver.fZIndex, 1, VertexAttribPointerType.UnsignedInt, false, DbVertex.Stride, DbVertex.ZIndexOffset);
|
||||||
|
GL.VertexAttribPointer(driver.v2TexPos, 2, VertexAttribPointerType.Float, false, DbVertex.Stride, DbVertex.TextureCoordinatesOffset);
|
||||||
|
GL.VertexAttribPointer(driver.fTexLayer, 1, VertexAttribPointerType.Float, false, DbVertex.Stride, DbVertex.TextureLayerOffset);
|
||||||
|
GL.VertexAttribPointer(driver.v4Color, 4, VertexAttribPointerType.Float, true, DbVertex.Stride, DbVertex.ColorOffset);
|
||||||
|
GL.EnableVertexAttribArray(driver.v2Position);
|
||||||
|
GL.EnableVertexAttribArray(driver.fZIndex);
|
||||||
|
GL.EnableVertexAttribArray(driver.v2TexPos);
|
||||||
|
GL.EnableVertexAttribArray(driver.v4Color);
|
||||||
|
GL.EnableVertexAttribArray(driver.fTexLayer);
|
||||||
|
|
||||||
|
GL.BindBuffer(BufferTarget.ElementArrayBuffer, ebo);
|
||||||
|
GL.BufferData(BufferTarget.ElementArrayBuffer, Queue.ElementCount * sizeof(int), Queue.ElementArray, BufferUsageHint.StreamDraw);
|
||||||
|
|
||||||
|
int Swap(ref int a, ref int b)
|
||||||
|
{
|
||||||
|
a ^= b;
|
||||||
|
b ^= a;
|
||||||
|
a ^= b;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool isDisposed;
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (isDisposed) return;
|
||||||
|
|
||||||
|
GL.DeleteVertexArray(VertexArray);
|
||||||
|
GL.DeleteBuffer(vbo1);
|
||||||
|
GL.DeleteBuffer(vbo2);
|
||||||
|
GL.DeleteBuffer(ebo1);
|
||||||
|
GL.DeleteBuffer(ebo2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class TextureManager : IDisposable
|
||||||
|
{
|
||||||
|
private readonly Dictionary<Image, int> textures = new Dictionary<Image, int>();
|
||||||
|
private readonly HashSet<Image> imagesNotUsed = new HashSet<Image>();
|
||||||
|
private bool isDisposed = false;
|
||||||
|
|
||||||
|
public void BeginFrame()
|
||||||
|
{
|
||||||
|
if (imagesNotUsed.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (Image image in imagesNotUsed)
|
||||||
|
{
|
||||||
|
GL.DeleteTexture(textures[image]);
|
||||||
|
}
|
||||||
|
|
||||||
|
imagesNotUsed.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Image image in textures.Keys)
|
||||||
|
{
|
||||||
|
imagesNotUsed.Add(image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetTexture(Image 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(Image image3d)
|
||||||
|
{
|
||||||
|
int texture = GL.GenTexture();
|
||||||
|
|
||||||
|
GL.BindTexture(TextureTarget.Texture2DArray, texture);
|
||||||
|
|
||||||
|
image3d.LockBits3d(out QImageLock lck, QImageLockOptions.Default);
|
||||||
|
GL.TexImage3D(TextureTarget.Texture2DArray, 0, PixelInternalFormat.Rgba, lck.Width, lck.Height, lck.Depth, 0, s_InternalFormat[lck.Format], s_PixelType[lck.Format], lck.ImagePtr);
|
||||||
|
image3d.UnlockBits();
|
||||||
|
|
||||||
|
GL.TexParameter(TextureTarget.Texture2DArray, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2DArray, TextureParameterName.TextureMagFilter, (int)TextureMinFilter.Linear);
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UploadTexture2d(Image image2d)
|
||||||
|
{
|
||||||
|
int texture = GL.GenTexture();
|
||||||
|
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, texture);
|
||||||
|
|
||||||
|
image2d.LockBits2d(out QImageLock lck, QImageLockOptions.Default);
|
||||||
|
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, lck.Width, lck.Height, 0, s_InternalFormat[lck.Format], s_PixelType[lck.Format], lck.ImagePtr);
|
||||||
|
image2d.UnlockBits();
|
||||||
|
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMinFilter.Linear);
|
||||||
|
|
||||||
|
switch (image2d.InternalFormat)
|
||||||
|
{
|
||||||
|
case ImageFormat.RedU8:
|
||||||
|
case ImageFormat.RedF:
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleR, (int)All.Red);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleG, (int)All.Red);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleB, (int)All.Red);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleA, (int)All.One);
|
||||||
|
break;
|
||||||
|
case ImageFormat.AlphaU8:
|
||||||
|
case ImageFormat.AlphaF:
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleR, (int)All.One);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleG, (int)All.One);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleB, (int)All.One);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleA, (int)All.Alpha);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (isDisposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
isDisposed = true;
|
||||||
|
|
||||||
|
int[] ids = textures.Values.ToArray();
|
||||||
|
GL.DeleteTextures(ids.Length, ref ids[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly Dictionary<ImageFormat, PixelFormat> s_InternalFormat = new Dictionary<ImageFormat, PixelFormat>()
|
||||||
|
{
|
||||||
|
[ImageFormat.AlphaF] = PixelFormat.Alpha,
|
||||||
|
[ImageFormat.AlphaU8] = PixelFormat.Alpha,
|
||||||
|
[ImageFormat.RedF] = PixelFormat.Red,
|
||||||
|
[ImageFormat.RedU8] = PixelFormat.Red,
|
||||||
|
[ImageFormat.RgbF] = PixelFormat.Rgb,
|
||||||
|
[ImageFormat.RgbU8] = PixelFormat.Rgb,
|
||||||
|
[ImageFormat.RgbaU8] = PixelFormat.Rgba,
|
||||||
|
[ImageFormat.RgbaF] = PixelFormat.Rgba,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly Dictionary<ImageFormat, PixelType> s_PixelType = new Dictionary<ImageFormat, PixelType>()
|
||||||
|
{
|
||||||
|
[ImageFormat.AlphaF] = PixelType.Float,
|
||||||
|
[ImageFormat.RedF] = PixelType.Float,
|
||||||
|
[ImageFormat.RgbF] = PixelType.Float,
|
||||||
|
[ImageFormat.RgbaF] = PixelType.Float,
|
||||||
|
[ImageFormat.AlphaU8] = PixelType.UnsignedByte,
|
||||||
|
[ImageFormat.RedU8] = PixelType.UnsignedByte,
|
||||||
|
[ImageFormat.RgbU8] = PixelType.UnsignedByte,
|
||||||
|
[ImageFormat.RgbaU8] = PixelType.UnsignedByte,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using static Quik.OpenGL.GLEnum;
|
using OpenTK.Graphics.OpenGL4;
|
||||||
|
|
||||||
namespace Quik.OpenGL
|
namespace Dashboard.OpenGL
|
||||||
{
|
{
|
||||||
[System.Serializable]
|
[System.Serializable]
|
||||||
public class GraphicsException : System.Exception
|
public class GraphicsException : Exception
|
||||||
{
|
{
|
||||||
public GraphicsException()
|
public GraphicsException()
|
||||||
{
|
{
|
||||||
@@ -16,7 +16,7 @@ namespace Quik.OpenGL
|
|||||||
AddExtraData();
|
AddExtraData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public GraphicsException(string message, System.Exception inner) : base(message, inner)
|
public GraphicsException(string message, Exception inner) : base(message, inner)
|
||||||
{
|
{
|
||||||
AddExtraData();
|
AddExtraData();
|
||||||
}
|
}
|
||||||
@@ -30,12 +30,12 @@ namespace Quik.OpenGL
|
|||||||
|
|
||||||
private void AddExtraData()
|
private void AddExtraData()
|
||||||
{
|
{
|
||||||
GL.Get(GL_MAJOR_VERSION, out int major);
|
GL.GetInteger(GetPName.MajorVersion, out int major);
|
||||||
GL.Get(GL_MINOR_VERSION, out int minor);
|
GL.GetInteger(GetPName.MinorVersion, out int minor);
|
||||||
|
|
||||||
string version = GL.GetString(GL_VERSION);
|
string version = GL.GetString(StringName.Version);
|
||||||
string vendor = GL.GetString(GL_VENDOR);
|
string vendor = GL.GetString(StringName.Vendor);
|
||||||
string renderer = GL.GetString(GL_RENDERER);
|
string renderer = GL.GetString(StringName.Renderer);
|
||||||
|
|
||||||
Data.Add("OpenGL Version", new Version(major, minor));
|
Data.Add("OpenGL Version", new Version(major, minor));
|
||||||
Data.Add("OpenGL Version String", version);
|
Data.Add("OpenGL Version String", version);
|
||||||
88
Dashboard/PAL/Dash.cs
Normal file
88
Dashboard/PAL/Dash.cs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
using Dashboard.ImmediateDraw;
|
||||||
|
using Dashboard.Controls;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Dashboard.PAL
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An abstraction layer over the UI input and output.
|
||||||
|
/// </summary>
|
||||||
|
public class Dash
|
||||||
|
{
|
||||||
|
private readonly IDashHandle handle;
|
||||||
|
private readonly IDbPlatform platform;
|
||||||
|
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get => platform.PortGetTitle(handle);
|
||||||
|
set => platform.PortSetTitle(handle, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2 Size
|
||||||
|
{
|
||||||
|
get => platform.PortGetSize(handle);
|
||||||
|
set => platform.PortSetSize(handle, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2 Position
|
||||||
|
{
|
||||||
|
get => platform.PortGetPosition(handle);
|
||||||
|
set => platform.PortSetPosition(handle, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UIBase? UIElement { get; set; }
|
||||||
|
|
||||||
|
public bool IsValid => platform.PortIsValid(handle);
|
||||||
|
|
||||||
|
public event EventHandler EventRaised
|
||||||
|
{
|
||||||
|
add
|
||||||
|
{
|
||||||
|
platform.PortSubscribeEvent(handle, value);
|
||||||
|
}
|
||||||
|
remove
|
||||||
|
{
|
||||||
|
platform.PortUnsubscribeEvent(handle, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dash(IDbPlatform platform)
|
||||||
|
{
|
||||||
|
this.platform = platform;
|
||||||
|
handle = platform.CreatePort();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDisposed = false;
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (isDisposed) return;
|
||||||
|
|
||||||
|
platform.DestroyPort(handle);
|
||||||
|
isDisposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Focus()
|
||||||
|
{
|
||||||
|
platform.PortFocus(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Paint(DrawList? list = null)
|
||||||
|
{
|
||||||
|
if (UIElement == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
list ??= new DrawList();
|
||||||
|
|
||||||
|
list.Clear();
|
||||||
|
UIElement.Bounds = new Rectangle(Size, new Vector2(0,0));
|
||||||
|
UIElement.Paint(list);
|
||||||
|
platform.PortPaint(handle, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Show(bool shown = true)
|
||||||
|
{
|
||||||
|
platform.PortShow(handle, shown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
Dashboard/PAL/IDbPlatform.cs
Normal file
62
Dashboard/PAL/IDbPlatform.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using Dashboard.ImmediateDraw;
|
||||||
|
using Dashboard.Media;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
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 IDbPlatform : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The title of the application.
|
||||||
|
/// </summary>
|
||||||
|
string? Title { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The default icon for the application.
|
||||||
|
/// </summary>
|
||||||
|
Image? 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);
|
||||||
|
Vector2 PortGetSize(IDashHandle port);
|
||||||
|
void PortSetSize(IDashHandle port, Vector2 size);
|
||||||
|
Vector2 PortGetPosition(IDashHandle port);
|
||||||
|
void PortSetPosition(IDashHandle port, Vector2 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, DrawList 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.Fonts;
|
||||||
|
|
||||||
|
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 Font font);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Quik.Media;
|
using Dashboard.Media;
|
||||||
using Quik.Typography;
|
using Dashboard.Media.Fonts;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
namespace Quik
|
namespace Dashboard
|
||||||
{
|
{
|
||||||
public enum TextAlignment
|
public enum TextAlignment
|
||||||
{
|
{
|
||||||
@@ -53,11 +54,11 @@ 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 Color4? Color
|
||||||
{
|
{
|
||||||
get => (QColor?)this["color"];
|
get => (Color4?)this["color"];
|
||||||
set => this["color"] = value;
|
set => this["color"] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,9 +110,9 @@ namespace Quik
|
|||||||
set => this["list-marker-position"] = value;
|
set => this["list-marker-position"] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QuikTexture ListMarkerImage
|
public Image? ListMarkerImage
|
||||||
{
|
{
|
||||||
get => (QuikTexture)this["list-marker-image"];
|
get => (Image?)this["list-marker-image"];
|
||||||
set => this["list-marker-image"] = value;
|
set => this["list-marker-image"] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,15 +122,15 @@ namespace Quik
|
|||||||
set => this["stroke-width"] = value;
|
set => this["stroke-width"] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QColor? StrokeColor
|
public OpenTK.Mathematics.Color4? StrokeColor
|
||||||
{
|
{
|
||||||
get => (QColor?)this["stroke-color"];
|
get => (Color4?)this["stroke-color"];
|
||||||
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 +145,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 +164,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--)
|
||||||
{
|
{
|
||||||
@@ -208,7 +209,7 @@ namespace Quik
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A line stipple pattern.
|
/// A line stipple pattern.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct QuikStipplePattern
|
public struct StipplePattern
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The stipple pitch value.
|
/// The stipple pitch value.
|
||||||
@@ -220,24 +221,24 @@ namespace Quik
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public float DutyCycle;
|
public float DutyCycle;
|
||||||
|
|
||||||
public QuikStipplePattern(float pitch, float dutyCycle)
|
public StipplePattern(float pitch, float dutyCycle)
|
||||||
{
|
{
|
||||||
Pitch = pitch;
|
Pitch = pitch;
|
||||||
DutyCycle = dutyCycle;
|
DutyCycle = dutyCycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static QuikStipplePattern None => new QuikStipplePattern(0.0f, 1.0f);
|
public static StipplePattern None => new StipplePattern(0.0f, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stroke style for lines and borders.
|
/// Stroke style for lines and borders.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class QuikStrokeStyle
|
public class StrokeStyle
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stroke color.
|
/// Stroke color.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public QColor Color { get; set; }
|
public Color4 Color { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stroke width.
|
/// Stroke width.
|
||||||
@@ -249,11 +250,11 @@ namespace Quik
|
|||||||
// /// </summary>
|
// /// </summary>
|
||||||
// public QuikStipplePattern StipplePattern { get; set; }
|
// public QuikStipplePattern StipplePattern { get; set; }
|
||||||
|
|
||||||
public QuikStrokeStyle()
|
public StrokeStyle()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public QuikStrokeStyle(QColor color, float width /*, QuikStipplePattern pattern*/)
|
public StrokeStyle(Color4 color, float width /*, QuikStipplePattern pattern*/)
|
||||||
{
|
{
|
||||||
Color = color;
|
Color = color;
|
||||||
Width = width;
|
Width = width;
|
||||||
@@ -268,8 +269,8 @@ namespace Quik
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fill style for rectangles and the like.
|
/// Fill style for rectangles and the like.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class QuikFillStyle
|
public class FillStyle
|
||||||
{
|
{
|
||||||
public QColor Color { get; set; }
|
public Color4 Color { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
108
Dashboard/Typography/FontProvider.cs
Normal file
108
Dashboard/Typography/FontProvider.cs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
using Dashboard.Media;
|
||||||
|
using Dashboard.Media.Fonts;
|
||||||
|
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, Font> Fonts { get; } = new Dictionary<FontFace, Font>();
|
||||||
|
private HashSet<Font> UsedFonts { get; } = new HashSet<Font>();
|
||||||
|
public readonly FontRasterizerOptions RasterizerOptions;
|
||||||
|
public IFontDataBase? Database { get; set; }
|
||||||
|
public IFontFactory? FontFactory { get; set; }
|
||||||
|
private readonly DbApplication App;
|
||||||
|
|
||||||
|
public Font this[FontFace info]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!Fonts.TryGetValue(info, out Font? 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 Font this[SystemFontFamily family]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this[Database?.GetSystemFontFace(family) ?? throw new Exception("No font database.")];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FontProvider(DbApplication 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(DbApplication 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 (Font font in Fonts.Values)
|
||||||
|
{
|
||||||
|
font.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
|
using OpenTK.Mathematics;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
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.
|
||||||
@@ -182,7 +183,7 @@ namespace Quik.Typography
|
|||||||
int index = 0;
|
int index = 0;
|
||||||
bool firstLine = true;
|
bool firstLine = true;
|
||||||
|
|
||||||
QVec2 pen = new QVec2(0, -PreSpace);
|
Vector2 pen = new Vector2(0, -PreSpace);
|
||||||
|
|
||||||
while (index < Blocks.Count)
|
while (index < Blocks.Count)
|
||||||
{
|
{
|
||||||
@@ -226,7 +227,7 @@ namespace Quik.Typography
|
|||||||
|
|
||||||
pen.Y -= PostSpace;
|
pen.Y -= PostSpace;
|
||||||
|
|
||||||
group.BoundingBox = new QRectangle(width, 0, 0, pen.Y);
|
group.BoundingBox = new Rectangle(width, pen.Y, 0, 0);
|
||||||
group.Translate(-pen);
|
group.Translate(-pen);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,9 +265,9 @@ namespace Quik.Typography
|
|||||||
TypesetGroup group,
|
TypesetGroup group,
|
||||||
Queue<HorizontalTextBlock> line,
|
Queue<HorizontalTextBlock> line,
|
||||||
float interblockWs,
|
float interblockWs,
|
||||||
ref QVec2 pen)
|
ref Vector2 pen)
|
||||||
{
|
{
|
||||||
QVec2 penpal = pen;
|
Vector2 penpal = pen;
|
||||||
|
|
||||||
while (line.TryDequeue(out HorizontalTextBlock block))
|
while (line.TryDequeue(out HorizontalTextBlock block))
|
||||||
{
|
{
|
||||||
@@ -353,15 +354,15 @@ namespace Quik.Typography
|
|||||||
public struct TypesetCharacter
|
public struct TypesetCharacter
|
||||||
{
|
{
|
||||||
public int Character;
|
public int Character;
|
||||||
public QuikTexture Texture;
|
public Image Texture;
|
||||||
public QRectangle Position;
|
public Rectangle Position;
|
||||||
public QRectangle UV;
|
public Rectangle UV;
|
||||||
|
|
||||||
public TypesetCharacter(
|
public TypesetCharacter(
|
||||||
int chr,
|
int chr,
|
||||||
QuikTexture texture,
|
Image texture,
|
||||||
in QRectangle position,
|
in Rectangle position,
|
||||||
in QRectangle uv)
|
in Rectangle uv)
|
||||||
{
|
{
|
||||||
Character = chr;
|
Character = chr;
|
||||||
Texture = texture;
|
Texture = texture;
|
||||||
@@ -375,7 +376,7 @@ namespace Quik.Typography
|
|||||||
private int _count = 0;
|
private int _count = 0;
|
||||||
private TypesetCharacter[] _array = Array.Empty<TypesetCharacter>();
|
private TypesetCharacter[] _array = Array.Empty<TypesetCharacter>();
|
||||||
|
|
||||||
public QRectangle BoundingBox;
|
public Rectangle BoundingBox;
|
||||||
|
|
||||||
public int Count => _count;
|
public int Count => _count;
|
||||||
|
|
||||||
@@ -396,7 +397,7 @@ namespace Quik.Typography
|
|||||||
_count = 0;
|
_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Translate(QVec2 offset)
|
public void Translate(Vector2 offset)
|
||||||
{
|
{
|
||||||
BoundingBox.Translate(offset);
|
BoundingBox.Translate(offset);
|
||||||
|
|
||||||
182
Dashboard/Typography/Typesetter.cs
Normal file
182
Dashboard/Typography/Typesetter.cs
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
using OpenTK.Mathematics;
|
||||||
|
using Dashboard.ImmediateDraw;
|
||||||
|
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 Vector2 MeasureHorizontal(ReadOnlySpan<char> str, float size, Font 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 Vector2(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void TypesetHorizontalDirect(this DrawList list, ReadOnlySpan<char> str, Vector2 origin, float size, Font font)
|
||||||
|
{
|
||||||
|
Dictionary<Image, FontDrawInfo> drawInfo = new Dictionary<Image, FontDrawInfo>();
|
||||||
|
var enumerator = new LineEnumerator(str);
|
||||||
|
|
||||||
|
Vector2 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 Vector2(0, rise);
|
||||||
|
|
||||||
|
foreach (Rune r in line.EnumerateRunes())
|
||||||
|
{
|
||||||
|
FontDrawInfo info;
|
||||||
|
int codepoint = r.Value;
|
||||||
|
|
||||||
|
font.Get(codepoint, size, out FontGlyph glyph);
|
||||||
|
ref readonly GlyphMetrics metrics = ref glyph.Metrics;
|
||||||
|
Image? image = glyph.Image;
|
||||||
|
|
||||||
|
if (image == null)
|
||||||
|
{
|
||||||
|
pen += new Vector2(metrics.Advance.X, 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drawInfo.TryGetValue(image, out info))
|
||||||
|
{
|
||||||
|
info = new FontDrawInfo();
|
||||||
|
info.Image = image;
|
||||||
|
info.rectangles = new List<Rectangle>();
|
||||||
|
drawInfo[image] = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle dest = new Rectangle(
|
||||||
|
pen + new Vector2(metrics.HorizontalBearing.X + metrics.Size.X, metrics.Size.Y - metrics.HorizontalBearing.Y),
|
||||||
|
pen + new Vector2(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 Image Image;
|
||||||
|
public List<Rectangle> 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,37 +1,44 @@
|
|||||||
|
using OpenTK.Mathematics;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Quik.VertexGenerator
|
namespace Dashboard.VertexGenerator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a GPU vertex.
|
/// Represents a GPU vertex.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DebuggerDisplay("XY={Position} RGBA={Color}, UV={TextureCoordinates}")]
|
[DebuggerDisplay("XY={Position} RGBA={Color}, UV={TextureCoordinates}")]
|
||||||
public struct QuikVertex
|
public struct DbVertex
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Position value.
|
/// Position value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public QVec2 Position;
|
public Vector2 Position;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Texture Coordinates.
|
/// Texture Coordinates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public QVec2 TextureCoordinates;
|
public Vector2 TextureCoordinates;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Per vertex color value.
|
/// Per vertex color value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public QColor Color;
|
public Color4 Color;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Per vertex depth index value.
|
/// Per vertex depth index value.
|
||||||
/// </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(Vector2);
|
||||||
public static unsafe int ColorOffset => 2 * sizeof(QVec2);
|
public static unsafe int ColorOffset => 2 * sizeof(Vector2);
|
||||||
public static unsafe int ZIndexOffset => ColorOffset + sizeof(QColor);
|
public static unsafe int ZIndexOffset => ColorOffset + sizeof(Color4);
|
||||||
public static unsafe int Stride => sizeof(QuikVertex);
|
public static unsafe int TextureLayerOffset => ZIndexOffset + sizeof(int);
|
||||||
|
public static unsafe int Stride => sizeof(DbVertex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,22 +1,23 @@
|
|||||||
|
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 DrawCallQueue : IEnumerable<DrawCall>
|
||||||
{
|
{
|
||||||
private readonly RefList<QuikVertex> _vertices = new RefList<QuikVertex>();
|
private readonly RefList<DbVertex> _vertices = new RefList<DbVertex>();
|
||||||
private readonly RefList<int> _elements = new RefList<int>();
|
private readonly RefList<int> _elements = new RefList<int>();
|
||||||
private readonly List<DrawCall> _drawCalls = new List<DrawCall>();
|
private readonly List<DrawCall> _drawCalls = new List<DrawCall>();
|
||||||
private int _start;
|
private int _start;
|
||||||
private int _baseOffset;
|
private int _baseOffset;
|
||||||
private QRectangle _bounds;
|
private Rectangle _bounds;
|
||||||
private QuikTexture _texture;
|
private Image? _texture;
|
||||||
|
|
||||||
public int ZDepth { get; private set; }
|
public int ZDepth { get; private set; }
|
||||||
public QuikVertex[] VertexArray => _vertices.InternalArray;
|
public DbVertex[] VertexArray => _vertices.InternalArray;
|
||||||
public int VertexCount => _vertices.Count;
|
public int VertexCount => _vertices.Count;
|
||||||
public int[] ElementArray => _elements.InternalArray;
|
public int[] ElementArray => _elements.InternalArray;
|
||||||
public int ElementCount => _elements.Count;
|
public int ElementCount => _elements.Count;
|
||||||
@@ -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 Rectangle bounds, Image? texture, int baseOffset)
|
||||||
{
|
{
|
||||||
_start = ElementCount;
|
_start = ElementCount;
|
||||||
_texture = texture;
|
_texture = texture;
|
||||||
@@ -41,16 +42,16 @@ namespace Quik.VertexGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void StartDrawCall(in QRectangle bounds) => StartDrawCall(bounds, null, _vertices.Count);
|
public void StartDrawCall(in Rectangle bounds) => StartDrawCall(bounds, null, _vertices.Count);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void StartDrawCall(in QRectangle bounds, int baseOffset) => StartDrawCall(bounds, null, baseOffset);
|
public void StartDrawCall(in Rectangle 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 Rectangle bounds, Image texture) => StartDrawCall(bounds, texture, _vertices.Count);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void AddVertex(in QuikVertex vertex)
|
public void AddVertex(in DbVertex vertex)
|
||||||
{
|
{
|
||||||
_vertices.Add(in vertex);
|
_vertices.Add(in vertex);
|
||||||
}
|
}
|
||||||
@@ -98,10 +99,10 @@ namespace Quik.VertexGenerator
|
|||||||
{
|
{
|
||||||
public int Start { get; }
|
public int Start { get; }
|
||||||
public int Count { get; }
|
public int Count { get; }
|
||||||
public QRectangle Bounds { get; }
|
public Rectangle Bounds { get; }
|
||||||
public QuikTexture Texture { get; }
|
public Image? Texture { get; }
|
||||||
|
|
||||||
public DrawCall(int start, int count, in QRectangle bounds, QuikTexture texture)
|
public DrawCall(int start, int count, in Rectangle bounds, Image? texture)
|
||||||
{
|
{
|
||||||
Start = start;
|
Start = start;
|
||||||
Count = count;
|
Count = count;
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">Container type.</typeparam>
|
/// <typeparam name="T">Container type.</typeparam>
|
||||||
public class RefList<T>
|
internal class RefList<T>
|
||||||
{
|
{
|
||||||
private T[] _array = Array.Empty<T>();
|
private T[] _array = Array.Empty<T>();
|
||||||
private int _count = 0;
|
private int _count = 0;
|
||||||
@@ -1,27 +1,38 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Quik.CommandMachine;
|
using Dashboard.ImmediateDraw;
|
||||||
|
using Dashboard.Media;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
namespace Quik.VertexGenerator
|
namespace Dashboard.VertexGenerator
|
||||||
{
|
{
|
||||||
public class VertexGeneratorEngine : CommandEngine
|
public class VertexDrawingEngine : DrawingEngine
|
||||||
{
|
{
|
||||||
public DrawQueue DrawQueue { get; } = new DrawQueue();
|
public DrawCallQueue DrawQueue { get; } = new DrawCallQueue();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Granularity for rounded geometry.
|
/// Granularity for rounded geometry.
|
||||||
/// </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 QuikVertex StrokeVertex => new QuikVertex()
|
protected bool BlendTextures =>
|
||||||
|
(Style["-vertex-blend-textures"] is bool value) ? value : false;
|
||||||
|
|
||||||
|
protected DbVertex StrokeVertex => new DbVertex()
|
||||||
{
|
{
|
||||||
ZIndex = Style.ZIndex ?? this.ZIndex,
|
ZIndex = Style.ZIndex ?? this.ZIndex,
|
||||||
Color = Style.StrokeColor ?? QColor.Black,
|
Color = Style.StrokeColor ?? Color4.Black,
|
||||||
};
|
};
|
||||||
protected QuikVertex FillVertex => new QuikVertex()
|
protected DbVertex FillVertex => new DbVertex()
|
||||||
{
|
{
|
||||||
ZIndex = Style.ZIndex ?? this.ZIndex,
|
ZIndex = Style.ZIndex ?? this.ZIndex,
|
||||||
Color = Style.Color ?? QColor.White,
|
Color = Style.Color ?? Color4.White,
|
||||||
|
};
|
||||||
|
|
||||||
|
protected DbVertex ImageVertex => new DbVertex()
|
||||||
|
{
|
||||||
|
ZIndex = Style.ZIndex ?? this.ZIndex,
|
||||||
|
Color = BlendTextures ? (Style.Color ?? Color4.White) : Color4.White,
|
||||||
};
|
};
|
||||||
|
|
||||||
public override void Reset()
|
public override void Reset()
|
||||||
@@ -30,7 +41,7 @@ namespace Quik.VertexGenerator
|
|||||||
DrawQueue.Clear();
|
DrawQueue.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ChildProcessCommand(Command name, CommandQueue queue)
|
protected override void ChildProcessCommand(Command name, DrawQueue 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,8 +66,8 @@ namespace Quik.VertexGenerator
|
|||||||
return (int) Math.Ceiling(arc * radius * CurveGranularity);
|
return (int) Math.Ceiling(arc * radius * CurveGranularity);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<QLine> LineList = new List<QLine>();
|
private readonly List<Line> LineList = new List<Line>();
|
||||||
private void LineProc(CommandQueue queue)
|
private void LineProc(DrawQueue queue)
|
||||||
{
|
{
|
||||||
Frame frame = queue.Dequeue();
|
Frame frame = queue.Dequeue();
|
||||||
|
|
||||||
@@ -67,12 +79,12 @@ namespace Quik.VertexGenerator
|
|||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
frame = queue.Dequeue();
|
frame = queue.Dequeue();
|
||||||
LineList.Add((QLine)frame);
|
LineList.Add((Line)frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LineList.Add((QLine)frame);
|
LineList.Add((Line)frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
float width = Style.StrokeWidth ?? 1;
|
float width = Style.StrokeWidth ?? 1;
|
||||||
@@ -81,7 +93,7 @@ namespace Quik.VertexGenerator
|
|||||||
LineInfo prevBase, nextBase = default;
|
LineInfo prevBase, nextBase = default;
|
||||||
for (int i = 0; i < LineList.Count; i++)
|
for (int i = 0; i < LineList.Count; i++)
|
||||||
{
|
{
|
||||||
QLine line = LineList[i];
|
Line line = LineList[i];
|
||||||
// A line segment needs a start cap if it is the first segment in
|
// A line segment needs a start cap if it is the first segment in
|
||||||
// the list, or the last end point is not the current start point.
|
// the list, or the last end point is not the current start point.
|
||||||
bool isStart = (i == 0 || line.Start != LineList[i - 1].End);
|
bool isStart = (i == 0 || line.Start != LineList[i - 1].End);
|
||||||
@@ -112,11 +124,11 @@ namespace Quik.VertexGenerator
|
|||||||
DrawQueue.EndDrawCall();
|
DrawQueue.EndDrawCall();
|
||||||
}
|
}
|
||||||
|
|
||||||
private LineInfo GenerateLineSegment(in QLine line)
|
private LineInfo GenerateLineSegment(in Line line)
|
||||||
{
|
{
|
||||||
QuikVertex vertex = StrokeVertex;
|
DbVertex vertex = StrokeVertex;
|
||||||
QuikVertex a, b, c, d;
|
DbVertex a, b, c, d;
|
||||||
QVec2 normal = line.Normal();
|
Vector2 normal = line.Normal();
|
||||||
float width = Style.StrokeWidth ?? 1;
|
float width = Style.StrokeWidth ?? 1;
|
||||||
|
|
||||||
a = b = c = d = vertex;
|
a = b = c = d = vertex;
|
||||||
@@ -136,20 +148,20 @@ namespace Quik.VertexGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateJoint(
|
private void GenerateJoint(
|
||||||
in QVec2 center,
|
in Vector2 center,
|
||||||
in QVec2 prevNormal,
|
in Vector2 prevNormal,
|
||||||
in QVec2 nextNormal,
|
in Vector2 nextNormal,
|
||||||
in LineInfo prevInfo,
|
in LineInfo prevInfo,
|
||||||
in LineInfo nextInfo)
|
in LineInfo nextInfo)
|
||||||
{
|
{
|
||||||
// Figure out which side needs the joint.
|
// Figure out which side needs the joint.
|
||||||
QVec2 meanNormal = 0.5f * (prevNormal + nextNormal);
|
Vector2 meanNormal = 0.5f * (prevNormal + nextNormal);
|
||||||
QVec2 meanTangent = new QVec2(meanNormal.Y, -meanNormal.X);
|
Vector2 meanTangent = new Vector2(meanNormal.Y, -meanNormal.X);
|
||||||
QVec2 positiveEdge = ((center + nextNormal) - (center + prevNormal)).Normalize();
|
Vector2 positiveEdge = ((center + nextNormal) - (center + prevNormal)).Normalized();
|
||||||
QVec2 negativeEdge = ((center - nextNormal) - (center - prevNormal)).Normalize();
|
Vector2 negativeEdge = ((center - nextNormal) - (center - prevNormal)).Normalized();
|
||||||
float positive, negative;
|
float positive, negative;
|
||||||
positive = QVec2.Dot(meanTangent, positiveEdge);
|
positive = Vector2.Dot(meanTangent, positiveEdge);
|
||||||
negative = QVec2.Dot(meanNormal, negativeEdge);
|
negative = Vector2.Dot(meanNormal, negativeEdge);
|
||||||
|
|
||||||
if (positive == negative)
|
if (positive == negative)
|
||||||
{
|
{
|
||||||
@@ -159,9 +171,9 @@ namespace Quik.VertexGenerator
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QuikVertex vertex = StrokeVertex;
|
DbVertex vertex = StrokeVertex;
|
||||||
float radius = Style.StrokeWidth/2 ?? 0.5f;
|
float radius = Style.StrokeWidth/2 ?? 0.5f;
|
||||||
float arc = MathF.Acos(QVec2.Dot(prevNormal, nextNormal));
|
float arc = MathF.Acos(Vector2.Dot(prevNormal, nextNormal));
|
||||||
int resolution = GetRoundingResolution(radius, arc);
|
int resolution = GetRoundingResolution(radius, arc);
|
||||||
bool isNegative = positive < negative;
|
bool isNegative = positive < negative;
|
||||||
|
|
||||||
@@ -187,10 +199,10 @@ namespace Quik.VertexGenerator
|
|||||||
float cos = MathF.Cos(angle);
|
float cos = MathF.Cos(angle);
|
||||||
float sin = MathF.Sin(angle);
|
float sin = MathF.Sin(angle);
|
||||||
|
|
||||||
QVec2 displacement;
|
Vector2 displacement;
|
||||||
if (isNegative)
|
if (isNegative)
|
||||||
{
|
{
|
||||||
displacement = new QVec2()
|
displacement = new Vector2()
|
||||||
{
|
{
|
||||||
X = -prevNormal.X * cos + prevNormal.Y * sin,
|
X = -prevNormal.X * cos + prevNormal.Y * sin,
|
||||||
Y = -prevNormal.X * sin - prevNormal.Y * cos
|
Y = -prevNormal.X * sin - prevNormal.Y * cos
|
||||||
@@ -198,7 +210,7 @@ namespace Quik.VertexGenerator
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
displacement = new QVec2()
|
displacement = new Vector2()
|
||||||
{
|
{
|
||||||
X = nextNormal.X * cos - nextNormal.Y * sin,
|
X = nextNormal.X * cos - nextNormal.Y * sin,
|
||||||
Y = nextNormal.X * sin + nextNormal.Y * cos
|
Y = nextNormal.X * sin + nextNormal.Y * cos
|
||||||
@@ -222,13 +234,13 @@ namespace Quik.VertexGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateCap(
|
private void GenerateCap(
|
||||||
in QVec2 center,
|
in Vector2 center,
|
||||||
in QVec2 normal,
|
in Vector2 normal,
|
||||||
in LineInfo info,
|
in LineInfo info,
|
||||||
bool endCap)
|
bool endCap)
|
||||||
{
|
{
|
||||||
int lastIndex, startIndex;
|
int lastIndex, startIndex;
|
||||||
QuikVertex vertex = StrokeVertex;
|
DbVertex vertex = StrokeVertex;
|
||||||
float radius = Style.StrokeWidth ?? 1.0f;
|
float radius = Style.StrokeWidth ?? 1.0f;
|
||||||
int resolution = GetRoundingResolution(radius, MathF.PI);
|
int resolution = GetRoundingResolution(radius, MathF.PI);
|
||||||
|
|
||||||
@@ -250,10 +262,10 @@ namespace Quik.VertexGenerator
|
|||||||
float cos = MathF.Cos(angle);
|
float cos = MathF.Cos(angle);
|
||||||
float sin = MathF.Sin(angle);
|
float sin = MathF.Sin(angle);
|
||||||
|
|
||||||
QVec2 displacement;
|
Vector2 displacement;
|
||||||
if (endCap)
|
if (endCap)
|
||||||
{
|
{
|
||||||
displacement = new QVec2()
|
displacement = new Vector2()
|
||||||
{
|
{
|
||||||
X = normal.X * cos + normal.Y * sin,
|
X = normal.X * cos + normal.Y * sin,
|
||||||
Y = -normal.X * sin + normal.Y * cos
|
Y = -normal.X * sin + normal.Y * cos
|
||||||
@@ -261,7 +273,7 @@ namespace Quik.VertexGenerator
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
displacement = new QVec2()
|
displacement = new Vector2()
|
||||||
{
|
{
|
||||||
X = normal.X * cos - normal.Y * sin,
|
X = normal.X * cos - normal.Y * sin,
|
||||||
Y = normal.X * sin + normal.Y * cos
|
Y = normal.X * sin + normal.Y * cos
|
||||||
@@ -279,8 +291,8 @@ namespace Quik.VertexGenerator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<QBezier> BezierList = new List<QBezier>();
|
private readonly List<Bezier> BezierList = new List<Bezier>();
|
||||||
private void BezierProc(CommandQueue queue)
|
private void BezierProc(DrawQueue queue)
|
||||||
{
|
{
|
||||||
Frame a = queue.Dequeue();
|
Frame a = queue.Dequeue();
|
||||||
Frame b;
|
Frame b;
|
||||||
@@ -296,11 +308,11 @@ namespace Quik.VertexGenerator
|
|||||||
b = queue.Dequeue();
|
b = queue.Dequeue();
|
||||||
|
|
||||||
BezierList.Add(
|
BezierList.Add(
|
||||||
new QBezier(
|
new Bezier(
|
||||||
new QVec2(a.GetF(0), a.GetF(1)),
|
new Vector2(a.GetF(0), a.GetF(1)),
|
||||||
new QVec2(b.GetF(0), b.GetF(1)),
|
new Vector2(b.GetF(0), b.GetF(1)),
|
||||||
new QVec2(b.GetF(2), b.GetF(3)),
|
new Vector2(b.GetF(2), b.GetF(3)),
|
||||||
new QVec2(a.GetF(2), a.GetF(3))
|
new Vector2(a.GetF(2), a.GetF(3))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -310,11 +322,11 @@ namespace Quik.VertexGenerator
|
|||||||
b = queue.Dequeue();
|
b = queue.Dequeue();
|
||||||
|
|
||||||
BezierList.Add(
|
BezierList.Add(
|
||||||
new QBezier(
|
new Bezier(
|
||||||
new QVec2(a.GetF(0), a.GetF(1)),
|
new Vector2(a.GetF(0), a.GetF(1)),
|
||||||
new QVec2(b.GetF(0), b.GetF(1)),
|
new Vector2(b.GetF(0), b.GetF(1)),
|
||||||
new QVec2(b.GetF(2), b.GetF(3)),
|
new Vector2(b.GetF(2), b.GetF(3)),
|
||||||
new QVec2(a.GetF(2), a.GetF(3))
|
new Vector2(a.GetF(2), a.GetF(3))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -325,7 +337,7 @@ namespace Quik.VertexGenerator
|
|||||||
LineInfo prevBase, nextBase = default;
|
LineInfo prevBase, nextBase = default;
|
||||||
for (int i = 0; i < LineList.Count; i++)
|
for (int i = 0; i < LineList.Count; i++)
|
||||||
{
|
{
|
||||||
QBezier bezier = BezierList[i];
|
Bezier bezier = BezierList[i];
|
||||||
// A line segment needs a start cap if it is the first segment in
|
// A line segment needs a start cap if it is the first segment in
|
||||||
// the list, or the last end point is not the current start point.
|
// the list, or the last end point is not the current start point.
|
||||||
bool isStart = (i == 0 || bezier.Start != BezierList[i - 1].End);
|
bool isStart = (i == 0 || bezier.Start != BezierList[i - 1].End);
|
||||||
@@ -356,19 +368,19 @@ namespace Quik.VertexGenerator
|
|||||||
DrawQueue.EndDrawCall();
|
DrawQueue.EndDrawCall();
|
||||||
}
|
}
|
||||||
|
|
||||||
private LineInfo GenerateBezierSegment(in QBezier bezier)
|
private LineInfo GenerateBezierSegment(in Bezier bezier)
|
||||||
{
|
{
|
||||||
QVec2 startTangent = bezier.GetBezierTangent(0);
|
Vector2 startTangent = bezier.GetBezierTangent(0);
|
||||||
QVec2 endTangent = bezier.GetBezierTangent(1);
|
Vector2 endTangent = bezier.GetBezierTangent(1);
|
||||||
QVec2 startNormal = new QVec2(-startTangent.Y, startTangent.X).Normalize();
|
Vector2 startNormal = new Vector2(-startTangent.Y, startTangent.X).Normalized();
|
||||||
QVec2 endNormal = new QVec2(-endTangent.Y, endTangent.X).Normalize();
|
Vector2 endNormal = new Vector2(-endTangent.Y, endTangent.X).Normalized();
|
||||||
|
|
||||||
float width = Style.StrokeWidth ?? 1;
|
float width = Style.StrokeWidth ?? 1;
|
||||||
float radius = 0.5f * width;
|
float radius = 0.5f * width;
|
||||||
int resolution = GetRoundingResolution(radius, bezier.RasterizationArc);
|
int resolution = GetRoundingResolution(radius, bezier.RasterizationArc);
|
||||||
|
|
||||||
DrawQueue.RestoreOffset();
|
DrawQueue.RestoreOffset();
|
||||||
QuikVertex v = StrokeVertex;
|
DbVertex v = StrokeVertex;
|
||||||
int vbase = DrawQueue.BaseOffset;
|
int vbase = DrawQueue.BaseOffset;
|
||||||
int index = 2;
|
int index = 2;
|
||||||
|
|
||||||
@@ -380,9 +392,9 @@ namespace Quik.VertexGenerator
|
|||||||
for (int i = 0; i < resolution; i++, index += 2)
|
for (int i = 0; i < resolution; i++, index += 2)
|
||||||
{
|
{
|
||||||
float t = (i + 1.0f) / resolution;
|
float t = (i + 1.0f) / resolution;
|
||||||
QVec2 at = bezier.GetBezierTangent(t).Normalize();
|
Vector2 at = bezier.GetBezierTangent(t).Normalized();
|
||||||
QVec2 a = bezier.GetBezierPoint(t);
|
Vector2 a = bezier.GetBezierPoint(t);
|
||||||
QVec2 an = radius * new QVec2(-at.Y, at.X);
|
Vector2 an = radius * new Vector2(-at.Y, at.X);
|
||||||
|
|
||||||
v.Position = a + an;
|
v.Position = a + an;
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
@@ -396,8 +408,8 @@ namespace Quik.VertexGenerator
|
|||||||
return new LineInfo(vbase, 0, 1, index - 2, index - 1);
|
return new LineInfo(vbase, 0, 1, index - 2, index - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<QRectangle> RectangleList = new List<QRectangle>();
|
private readonly List<Rectangle> RectangleList = new List<Rectangle>();
|
||||||
private void RectangleProc(CommandQueue queue)
|
private void RectangleProc(DrawQueue queue)
|
||||||
{
|
{
|
||||||
Frame frame = queue.Dequeue();
|
Frame frame = queue.Dequeue();
|
||||||
RectangleList.Clear();
|
RectangleList.Clear();
|
||||||
@@ -407,12 +419,12 @@ namespace Quik.VertexGenerator
|
|||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
frame = queue.Dequeue();
|
frame = queue.Dequeue();
|
||||||
RectangleList.Add((QRectangle)frame);
|
RectangleList.Add((Rectangle)frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
RectangleList.Add((QRectangle)frame);
|
RectangleList.Add((Rectangle)frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
float stroke = Style.StrokeWidth ?? 1.0f;
|
float stroke = Style.StrokeWidth ?? 1.0f;
|
||||||
@@ -420,10 +432,10 @@ namespace Quik.VertexGenerator
|
|||||||
DrawQueue.StartDrawCall(Viewport);
|
DrawQueue.StartDrawCall(Viewport);
|
||||||
for (int i = 0; i < RectangleList.Count; i++)
|
for (int i = 0; i < RectangleList.Count; i++)
|
||||||
{
|
{
|
||||||
QRectangle outer = RectangleList[i];
|
Rectangle outer = RectangleList[i];
|
||||||
QRectangle inner = new QRectangle(
|
Rectangle inner = new Rectangle(
|
||||||
outer.Right - stroke, outer.Top - stroke,
|
outer.Right - stroke, outer.Bottom - stroke,
|
||||||
outer.Left + stroke, outer.Bottom + stroke);
|
outer.Left + stroke, outer.Top + stroke);
|
||||||
|
|
||||||
GenerateRectangleBase(inner, Math.Max(radius - stroke, 0.0f));
|
GenerateRectangleBase(inner, Math.Max(radius - stroke, 0.0f));
|
||||||
|
|
||||||
@@ -445,7 +457,7 @@ namespace Quik.VertexGenerator
|
|||||||
DrawQueue.EndDrawCall();
|
DrawQueue.EndDrawCall();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateRectangleBase(in QRectangle rectangle, float radius)
|
private void GenerateRectangleBase(in Rectangle rectangle, float radius)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
+--j-------i--+
|
+--j-------i--+
|
||||||
@@ -466,16 +478,16 @@ namespace Quik.VertexGenerator
|
|||||||
|
|
||||||
// Draw center rectangle.
|
// Draw center rectangle.
|
||||||
|
|
||||||
QVec2 aPos, bPos, cPos, dPos;
|
Vector2 aPos, bPos, cPos, dPos;
|
||||||
|
|
||||||
QuikVertex v = FillVertex;
|
DbVertex v = FillVertex;
|
||||||
aPos = v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom + radius);
|
aPos = v.Position = new Vector2(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 Vector2(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 Vector2(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 Vector2(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);
|
||||||
@@ -486,9 +498,9 @@ namespace Quik.VertexGenerator
|
|||||||
|
|
||||||
// Draw south rectangle.
|
// Draw south rectangle.
|
||||||
|
|
||||||
v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom);
|
v.Position = new Vector2(rectangle.Left + radius, rectangle.Bottom);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom);
|
v.Position = new Vector2(rectangle.Right - radius, rectangle.Bottom);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
DrawQueue.AddElement(4); DrawQueue.AddElement(5); DrawQueue.AddElement(1);
|
DrawQueue.AddElement(4); DrawQueue.AddElement(5); DrawQueue.AddElement(1);
|
||||||
@@ -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 Vector2(rectangle.Right, rectangle.Bottom - radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right, rectangle.Top - radius);
|
v.Position = new Vector2(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);
|
||||||
@@ -506,9 +518,9 @@ namespace Quik.VertexGenerator
|
|||||||
|
|
||||||
// Draw north rectangle.
|
// Draw north rectangle.
|
||||||
|
|
||||||
v.Position = new QVec2(rectangle.Right - radius, rectangle.Top);
|
v.Position = new Vector2(rectangle.Right - radius, rectangle.Top);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left + radius, rectangle.Top);
|
v.Position = new Vector2(rectangle.Left + radius, rectangle.Top);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
DrawQueue.AddElement(3); DrawQueue.AddElement(2); DrawQueue.AddElement(8);
|
DrawQueue.AddElement(3); DrawQueue.AddElement(2); DrawQueue.AddElement(8);
|
||||||
@@ -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 Vector2(rectangle.Left, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left, rectangle.Bottom + radius);
|
v.Position = new Vector2(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);
|
||||||
@@ -535,7 +547,7 @@ namespace Quik.VertexGenerator
|
|||||||
float xoff = MathF.Cos(theta) * radius;
|
float xoff = MathF.Cos(theta) * radius;
|
||||||
float yoff = MathF.Sin(theta) * radius;
|
float yoff = MathF.Sin(theta) * radius;
|
||||||
|
|
||||||
v.Position = cPos + new QVec2(xoff, yoff);
|
v.Position = cPos + new Vector2(xoff, yoff);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
DrawQueue.AddElement(2); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
|
DrawQueue.AddElement(2); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
|
||||||
}
|
}
|
||||||
@@ -550,7 +562,7 @@ namespace Quik.VertexGenerator
|
|||||||
float xoff = -MathF.Sin(theta) * radius;
|
float xoff = -MathF.Sin(theta) * radius;
|
||||||
float yoff = MathF.Cos(theta) * radius;
|
float yoff = MathF.Cos(theta) * radius;
|
||||||
|
|
||||||
v.Position = dPos + new QVec2(xoff, yoff);
|
v.Position = dPos + new Vector2(xoff, yoff);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
DrawQueue.AddElement(3); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
|
DrawQueue.AddElement(3); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
|
||||||
}
|
}
|
||||||
@@ -565,7 +577,7 @@ namespace Quik.VertexGenerator
|
|||||||
float xoff = -MathF.Cos(theta) * radius;
|
float xoff = -MathF.Cos(theta) * radius;
|
||||||
float yoff = -MathF.Sin(theta) * radius;
|
float yoff = -MathF.Sin(theta) * radius;
|
||||||
|
|
||||||
v.Position = aPos + new QVec2(xoff, yoff);
|
v.Position = aPos + new Vector2(xoff, yoff);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
DrawQueue.AddElement(0); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
|
DrawQueue.AddElement(0); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
|
||||||
}
|
}
|
||||||
@@ -580,14 +592,14 @@ namespace Quik.VertexGenerator
|
|||||||
float xoff = -MathF.Sin(theta) * radius;
|
float xoff = -MathF.Sin(theta) * radius;
|
||||||
float yoff = MathF.Cos(theta) * radius;
|
float yoff = MathF.Cos(theta) * radius;
|
||||||
|
|
||||||
v.Position = bPos + new QVec2(xoff, yoff);
|
v.Position = bPos + new Vector2(xoff, yoff);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
DrawQueue.AddElement(1); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
|
DrawQueue.AddElement(1); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
|
||||||
}
|
}
|
||||||
DrawQueue.AddElement(1); DrawQueue.AddElement(previous); DrawQueue.AddElement(6);
|
DrawQueue.AddElement(1); DrawQueue.AddElement(previous); DrawQueue.AddElement(6);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateRectangleStripStraight(in QRectangle rectangle)
|
private void GenerateRectangleStripStraight(in Rectangle rectangle)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
h---------g
|
h---------g
|
||||||
@@ -604,27 +616,27 @@ namespace Quik.VertexGenerator
|
|||||||
0 1 2 3 4 5 6 7
|
0 1 2 3 4 5 6 7
|
||||||
*/
|
*/
|
||||||
|
|
||||||
QuikVertex v = StrokeVertex;
|
DbVertex v = StrokeVertex;
|
||||||
float stroke = Style.StrokeWidth ?? 1.0f;
|
float stroke = Style.StrokeWidth ?? 1.0f;
|
||||||
|
|
||||||
DrawQueue.RestoreOffset();
|
DrawQueue.RestoreOffset();
|
||||||
|
|
||||||
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Bottom + stroke);
|
v.Position = new Vector2(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 Vector2(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 Vector2(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 Vector2(rectangle.Left + stroke, rectangle.Top + stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
v.Position = new QVec2(rectangle.Left, rectangle.Bottom);
|
v.Position = new Vector2(rectangle.Left, rectangle.Bottom);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right, rectangle.Bottom);
|
v.Position = new Vector2(rectangle.Right, rectangle.Bottom);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right, rectangle.Top);
|
v.Position = new Vector2(rectangle.Right, rectangle.Top);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left, rectangle.Top);
|
v.Position = new Vector2(rectangle.Left, rectangle.Top);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
DrawQueue.AddElement(4); DrawQueue.AddElement(5); DrawQueue.AddElement(1); // SSW
|
DrawQueue.AddElement(4); DrawQueue.AddElement(5); DrawQueue.AddElement(1); // SSW
|
||||||
@@ -637,7 +649,7 @@ namespace Quik.VertexGenerator
|
|||||||
DrawQueue.AddElement(4); DrawQueue.AddElement(3); DrawQueue.AddElement(7); // SWW
|
DrawQueue.AddElement(4); DrawQueue.AddElement(3); DrawQueue.AddElement(7); // SWW
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateRectangleStripNarrow(in QRectangle rectangle, float radius)
|
private void GenerateRectangleStripNarrow(in Rectangle rectangle, float radius)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
v-j---i-u
|
v-j---i-u
|
||||||
@@ -661,74 +673,74 @@ namespace Quik.VertexGenerator
|
|||||||
u v w x
|
u v w x
|
||||||
20: 0 1 2 3
|
20: 0 1 2 3
|
||||||
*/
|
*/
|
||||||
QuikVertex v = StrokeVertex;
|
DbVertex v = StrokeVertex;
|
||||||
QVec2 nPos, qPos, tPos, wPos;
|
Vector2 nPos, qPos, tPos, wPos;
|
||||||
float stroke = Style.StrokeWidth ?? 1.0f;
|
float stroke = Style.StrokeWidth ?? 1.0f;
|
||||||
|
|
||||||
DrawQueue.RestoreOffset();
|
DrawQueue.RestoreOffset();
|
||||||
|
|
||||||
// a-b-c-d
|
// a-b-c-d
|
||||||
|
|
||||||
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Bottom + stroke);
|
v.Position = new Vector2(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 Vector2(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 Vector2(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 Vector2(rectangle.Left + stroke, rectangle.Top + stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
// ef-gh-ij-kl
|
// ef-gh-ij-kl
|
||||||
|
|
||||||
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Bottom);
|
v.Position = new Vector2(rectangle.Left + stroke, rectangle.Bottom);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Bottom);
|
v.Position = new Vector2(rectangle.Left + stroke, rectangle.Bottom);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right, rectangle.Bottom + stroke);
|
v.Position = new Vector2(rectangle.Right, rectangle.Bottom - stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right, rectangle.Top - stroke);
|
v.Position = new Vector2(rectangle.Right, rectangle.Top + stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Top);
|
v.Position = new Vector2(rectangle.Right - stroke, rectangle.Top);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Top);
|
v.Position = new Vector2(rectangle.Right - stroke, rectangle.Top);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left, rectangle.Top - stroke);
|
v.Position = new Vector2(rectangle.Left, rectangle.Top + stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left, rectangle.Bottom + stroke);
|
v.Position = new Vector2(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 Vector2(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 Vector2(rectangle.Left + radius, rectangle.Bottom - radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom);
|
v.Position = new Vector2(rectangle.Left + radius, rectangle.Bottom);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
// pqr
|
// pqr
|
||||||
v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom);
|
v.Position = new Vector2(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 Vector2(rectangle.Right - radius, rectangle.Bottom - radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right, rectangle.Bottom + radius);
|
v.Position = new Vector2(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 Vector2(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 Vector2(rectangle.Right - radius, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - radius, rectangle.Top);
|
v.Position = new Vector2(rectangle.Right - radius, rectangle.Top);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
// vwx
|
// vwx
|
||||||
|
|
||||||
v.Position = new QVec2(rectangle.Left + radius, rectangle.Top);
|
v.Position = new Vector2(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 Vector2(rectangle.Left + radius, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left, rectangle.Top - radius);
|
v.Position = new Vector2(rectangle.Left, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
// E
|
// E
|
||||||
@@ -779,7 +791,7 @@ namespace Quik.VertexGenerator
|
|||||||
float xoff = MathF.Cos(theta) * radius;
|
float xoff = MathF.Cos(theta) * radius;
|
||||||
float yoff = MathF.Sin(theta) * radius;
|
float yoff = MathF.Sin(theta) * radius;
|
||||||
|
|
||||||
v.Position = tPos + new QVec2(xoff, yoff);
|
v.Position = tPos + new Vector2(xoff, yoff);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
DrawQueue.AddElement(19); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
|
DrawQueue.AddElement(19); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
|
||||||
}
|
}
|
||||||
@@ -794,7 +806,7 @@ namespace Quik.VertexGenerator
|
|||||||
float xoff = -MathF.Sin(theta) * radius;
|
float xoff = -MathF.Sin(theta) * radius;
|
||||||
float yoff = MathF.Cos(theta) * radius;
|
float yoff = MathF.Cos(theta) * radius;
|
||||||
|
|
||||||
v.Position = wPos + new QVec2(xoff, yoff);
|
v.Position = wPos + new Vector2(xoff, yoff);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
DrawQueue.AddElement(22); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
|
DrawQueue.AddElement(22); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
|
||||||
}
|
}
|
||||||
@@ -809,7 +821,7 @@ namespace Quik.VertexGenerator
|
|||||||
float xoff = -MathF.Cos(theta) * radius;
|
float xoff = -MathF.Cos(theta) * radius;
|
||||||
float yoff = -MathF.Sin(theta) * radius;
|
float yoff = -MathF.Sin(theta) * radius;
|
||||||
|
|
||||||
v.Position = nPos + new QVec2(xoff, yoff);
|
v.Position = nPos + new Vector2(xoff, yoff);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
DrawQueue.AddElement(23); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
|
DrawQueue.AddElement(23); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
|
||||||
}
|
}
|
||||||
@@ -824,14 +836,14 @@ namespace Quik.VertexGenerator
|
|||||||
float xoff = -MathF.Sin(theta) * radius;
|
float xoff = -MathF.Sin(theta) * radius;
|
||||||
float yoff = MathF.Cos(theta) * radius;
|
float yoff = MathF.Cos(theta) * radius;
|
||||||
|
|
||||||
v.Position = qPos + new QVec2(xoff, yoff);
|
v.Position = qPos + new Vector2(xoff, yoff);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
DrawQueue.AddElement(16); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
|
DrawQueue.AddElement(16); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
|
||||||
}
|
}
|
||||||
DrawQueue.AddElement(16); DrawQueue.AddElement(previous); DrawQueue.AddElement(17);
|
DrawQueue.AddElement(16); DrawQueue.AddElement(previous); DrawQueue.AddElement(17);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateRectangleStripWide(in QRectangle rectangle, float radius)
|
private void GenerateRectangleStripWide(in Rectangle rectangle, float radius)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
l---k
|
l---k
|
||||||
@@ -850,45 +862,45 @@ namespace Quik.VertexGenerator
|
|||||||
10: 0 1 2 3 4 5
|
10: 0 1 2 3 4 5
|
||||||
*/
|
*/
|
||||||
|
|
||||||
QuikVertex v = StrokeVertex;
|
DbVertex v = StrokeVertex;
|
||||||
float stroke = Style.StrokeWidth ?? 1.0f;
|
float stroke = Style.StrokeWidth ?? 1.0f;
|
||||||
float innerRadius = radius - stroke;
|
float innerRadius = radius - stroke;
|
||||||
DrawQueue.RestoreOffset();
|
DrawQueue.RestoreOffset();
|
||||||
|
|
||||||
v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom);
|
v.Position = new Vector2(rectangle.Left + radius, rectangle.Bottom);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom);
|
v.Position = new Vector2(rectangle.Right - radius, rectangle.Bottom);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom + stroke);
|
v.Position = new Vector2(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 Vector2(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 Vector2(rectangle.Right - stroke, rectangle.Bottom - radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right, rectangle.Top - radius);
|
v.Position = new Vector2(rectangle.Right, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right, rectangle.Top - radius);
|
v.Position = new Vector2(rectangle.Right, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Bottom + radius);
|
v.Position = new Vector2(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 Vector2(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 Vector2(rectangle.Right - radius, rectangle.Top + stroke);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Right - radius, rectangle.Top);
|
v.Position = new Vector2(rectangle.Right - radius, rectangle.Top);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left + radius, rectangle.Top);
|
v.Position = new Vector2(rectangle.Left + radius, rectangle.Top);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
v.Position = new QVec2(rectangle.Left, rectangle.Bottom + radius);
|
v.Position = new Vector2(rectangle.Left, rectangle.Bottom + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Top - radius);
|
v.Position = new Vector2(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 Vector2(rectangle.Left + stroke, rectangle.Top + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = new QVec2(rectangle.Left, rectangle.Bottom + radius);
|
v.Position = new Vector2(rectangle.Left, rectangle.Bottom + radius);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
// S
|
// S
|
||||||
@@ -908,7 +920,7 @@ namespace Quik.VertexGenerator
|
|||||||
int resolution = GetRoundingResolution(radius, 0.5f * MathF.PI);
|
int resolution = GetRoundingResolution(radius, 0.5f * MathF.PI);
|
||||||
int current = 16;
|
int current = 16;
|
||||||
|
|
||||||
QVec2 center = new QVec2(rectangle.Right - radius, rectangle.Top - radius);
|
Vector2 center = new Vector2(rectangle.Right - radius, rectangle.Top - radius);
|
||||||
int s1 = 7, s2 = 6;
|
int s1 = 7, s2 = 6;
|
||||||
for (int i = 0; i < resolution - 1; i++)
|
for (int i = 0; i < resolution - 1; i++)
|
||||||
{
|
{
|
||||||
@@ -916,9 +928,9 @@ namespace Quik.VertexGenerator
|
|||||||
float xoff = MathF.Cos(theta);
|
float xoff = MathF.Cos(theta);
|
||||||
float yoff = MathF.Sin(theta);
|
float yoff = MathF.Sin(theta);
|
||||||
|
|
||||||
v.Position = center + radius * new QVec2(xoff, yoff);
|
v.Position = center + radius * new Vector2(xoff, yoff);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = center + innerRadius * new QVec2(xoff, yoff);
|
v.Position = center + innerRadius * new Vector2(xoff, yoff);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(current + 0);
|
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(current + 0);
|
||||||
@@ -931,7 +943,7 @@ namespace Quik.VertexGenerator
|
|||||||
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(9);
|
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(9);
|
||||||
|
|
||||||
// Draw NW arc
|
// Draw NW arc
|
||||||
center = new QVec2(rectangle.Left + radius, rectangle.Top - radius);
|
center = new Vector2(rectangle.Left + radius, rectangle.Top - radius);
|
||||||
s1 = 8; s2 = 11;
|
s1 = 8; s2 = 11;
|
||||||
for (int i = 0; i < resolution - 1; i++)
|
for (int i = 0; i < resolution - 1; i++)
|
||||||
{
|
{
|
||||||
@@ -939,9 +951,9 @@ namespace Quik.VertexGenerator
|
|||||||
float xoff = -MathF.Sin(theta);
|
float xoff = -MathF.Sin(theta);
|
||||||
float yoff = MathF.Cos(theta);
|
float yoff = MathF.Cos(theta);
|
||||||
|
|
||||||
v.Position = center + radius * new QVec2(xoff, yoff);
|
v.Position = center + radius * new Vector2(xoff, yoff);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = center + innerRadius * new QVec2(xoff, yoff);
|
v.Position = center + innerRadius * new Vector2(xoff, yoff);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(current + 0);
|
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(current + 0);
|
||||||
@@ -954,7 +966,7 @@ namespace Quik.VertexGenerator
|
|||||||
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(14);
|
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(14);
|
||||||
|
|
||||||
// Draw SW arc
|
// Draw SW arc
|
||||||
center = new QVec2(rectangle.Left + radius, rectangle.Bottom + radius);
|
center = new Vector2(rectangle.Left + radius, rectangle.Bottom + radius);
|
||||||
s1 = 13; s2 = 12;
|
s1 = 13; s2 = 12;
|
||||||
for (int i = 0; i < resolution - 1; i++)
|
for (int i = 0; i < resolution - 1; i++)
|
||||||
{
|
{
|
||||||
@@ -962,9 +974,9 @@ namespace Quik.VertexGenerator
|
|||||||
float xoff = -MathF.Cos(theta);
|
float xoff = -MathF.Cos(theta);
|
||||||
float yoff = -MathF.Sin(theta);
|
float yoff = -MathF.Sin(theta);
|
||||||
|
|
||||||
v.Position = center + radius * new QVec2(xoff, yoff);
|
v.Position = center + radius * new Vector2(xoff, yoff);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = center + innerRadius * new QVec2(xoff, yoff);
|
v.Position = center + innerRadius * new Vector2(xoff, yoff);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(current + 0);
|
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(current + 0);
|
||||||
@@ -977,7 +989,7 @@ namespace Quik.VertexGenerator
|
|||||||
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(3);
|
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(3);
|
||||||
|
|
||||||
// Draw SW arc
|
// Draw SW arc
|
||||||
center = new QVec2(rectangle.Right - radius, rectangle.Bottom + radius);
|
center = new Vector2(rectangle.Right - radius, rectangle.Bottom + radius);
|
||||||
s1 = 2; s2 = 1;
|
s1 = 2; s2 = 1;
|
||||||
for (int i = 0; i < resolution - 1; i++)
|
for (int i = 0; i < resolution - 1; i++)
|
||||||
{
|
{
|
||||||
@@ -985,9 +997,9 @@ namespace Quik.VertexGenerator
|
|||||||
float xoff = MathF.Sin(theta);
|
float xoff = MathF.Sin(theta);
|
||||||
float yoff = -MathF.Cos(theta);
|
float yoff = -MathF.Cos(theta);
|
||||||
|
|
||||||
v.Position = center + radius * new QVec2(xoff, yoff);
|
v.Position = center + radius * new Vector2(xoff, yoff);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
v.Position = center + innerRadius * new QVec2(xoff, yoff);
|
v.Position = center + innerRadius * new Vector2(xoff, yoff);
|
||||||
DrawQueue.AddVertex(v);
|
DrawQueue.AddVertex(v);
|
||||||
|
|
||||||
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(current + 0);
|
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(current + 0);
|
||||||
@@ -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(DrawQueue queue)
|
||||||
|
{
|
||||||
|
Frame frame = queue.Dequeue();
|
||||||
|
ImageCommandFlags flags = (ImageCommandFlags)frame.I1;
|
||||||
|
Image image = queue.Dequeue().As<Image>();
|
||||||
|
|
||||||
|
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(DrawQueue queue, Image image, int count, bool uv)
|
||||||
|
{
|
||||||
|
DrawQueue.StartDrawCall(Viewport, image);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
Rectangle rect = (Rectangle)queue.Dequeue();
|
||||||
|
Rectangle uvs;
|
||||||
|
|
||||||
|
if (uv)
|
||||||
|
{
|
||||||
|
uvs = (Rectangle)queue.Dequeue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uvs = new Rectangle(1, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawQueue.RestoreOffset();
|
||||||
|
DbVertex vertex = ImageVertex;
|
||||||
|
|
||||||
|
vertex.Position = new Vector2(rect.Left, rect.Top);
|
||||||
|
vertex.TextureCoordinates = new Vector2(uvs.Left, uvs.Top);
|
||||||
|
DrawQueue.AddVertex(vertex);
|
||||||
|
|
||||||
|
vertex.Position = new Vector2(rect.Left, rect.Bottom);
|
||||||
|
vertex.TextureCoordinates = new Vector2(uvs.Left, uvs.Bottom);
|
||||||
|
DrawQueue.AddVertex(vertex);
|
||||||
|
|
||||||
|
vertex.Position = new Vector2(rect.Right, rect.Bottom);
|
||||||
|
vertex.TextureCoordinates = new Vector2(uvs.Right, uvs.Bottom);
|
||||||
|
DrawQueue.AddVertex(vertex);
|
||||||
|
|
||||||
|
vertex.Position = new Vector2(rect.Right, rect.Top);
|
||||||
|
vertex.TextureCoordinates = new Vector2(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(DrawQueue queue, Image image, int count)
|
||||||
|
{
|
||||||
|
DrawQueue.StartDrawCall(Viewport, image);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
Rectangle rect = (Rectangle)queue.Dequeue();
|
||||||
|
Rectangle uvs = (Rectangle)queue.Dequeue();
|
||||||
|
int layer = (int)queue.Dequeue();
|
||||||
|
|
||||||
|
DrawQueue.RestoreOffset();
|
||||||
|
DbVertex vertex = ImageVertex;
|
||||||
|
vertex.TextureLayer = layer;
|
||||||
|
|
||||||
|
vertex.Position = new Vector2(rect.Top, rect.Left);
|
||||||
|
vertex.TextureCoordinates = new Vector2(uvs.Top, uvs.Left);
|
||||||
|
DrawQueue.AddVertex(vertex);
|
||||||
|
|
||||||
|
vertex.Position = new Vector2(rect.Bottom, rect.Left);
|
||||||
|
vertex.TextureCoordinates = new Vector2(uvs.Bottom, uvs.Left);
|
||||||
|
DrawQueue.AddVertex(vertex);
|
||||||
|
|
||||||
|
vertex.Position = new Vector2(rect.Bottom, rect.Right);
|
||||||
|
vertex.TextureCoordinates = new Vector2(uvs.Bottom, uvs.Right);
|
||||||
|
DrawQueue.AddVertex(vertex);
|
||||||
|
|
||||||
|
vertex.Position = new Vector2(rect.Top, rect.Right);
|
||||||
|
vertex.TextureCoordinates = new Vector2(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;
|
||||||
|
}
|
||||||
60
Dockerfile
60
Dockerfile
@@ -1,60 +0,0 @@
|
|||||||
# This is going to create an environment for you to cross compile all the
|
|
||||||
# packages needed to build this project.
|
|
||||||
#
|
|
||||||
# As always, debian > ubuntu <3
|
|
||||||
FROM debian:stable-slim
|
|
||||||
WORKDIR /root
|
|
||||||
|
|
||||||
# Download and Install dependencies.
|
|
||||||
# Install WGET
|
|
||||||
RUN apt-get update
|
|
||||||
RUN apt-get install -y sudo wget
|
|
||||||
|
|
||||||
# Add the .NET package repository to the repository listing.
|
|
||||||
RUN wget https://packages.microsoft.com/config/debian/11/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
|
|
||||||
RUN dpkg -i packages-microsoft-prod.deb
|
|
||||||
RUN rm packages-microsoft-prod.deb
|
|
||||||
|
|
||||||
# APT dependencies.
|
|
||||||
RUN apt-get update
|
|
||||||
RUN apt-get install -y \
|
|
||||||
build-essential \
|
|
||||||
bzip2 \
|
|
||||||
cmake \
|
|
||||||
clang \
|
|
||||||
cpio \
|
|
||||||
dotnet-sdk-6.0 \
|
|
||||||
gcc-arm-linux-gnueabihf \
|
|
||||||
gcc-aarch64-linux-gnu \
|
|
||||||
gcc-i686-linux-gnu \
|
|
||||||
git \
|
|
||||||
libssl-dev \
|
|
||||||
libxml2-dev \
|
|
||||||
lzma-dev \
|
|
||||||
mingw-w64 \
|
|
||||||
nuget \
|
|
||||||
ninja-build \
|
|
||||||
patch \
|
|
||||||
python3 \
|
|
||||||
xz-utils \
|
|
||||||
zlib1g-dev
|
|
||||||
|
|
||||||
# Clone osxcross
|
|
||||||
# Let's do this later.
|
|
||||||
# RUN git clone https://github.com/tpoechtrager/osxcross.git osxcross
|
|
||||||
|
|
||||||
# Setup interactive shell.
|
|
||||||
# Setup sudo. Remove password prompt for group "wheel".
|
|
||||||
RUN echo "%wheel ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/quik_sudo_conf
|
|
||||||
|
|
||||||
# Create a default user and switch.
|
|
||||||
RUN adduser --comment "" --disabled-password quik
|
|
||||||
USER quik
|
|
||||||
WORKDIR /home/quik
|
|
||||||
|
|
||||||
# Copy bashrc
|
|
||||||
RUN cp /etc/bash.bashrc ~/.bashrc
|
|
||||||
RUN echo source $HOME/src/sh/bashrc.sh >> ~/.bashrc
|
|
||||||
|
|
||||||
# Execute an interactive shell.
|
|
||||||
CMD bash
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
|
||||||
<Nullable>disable</Nullable>
|
|
||||||
<LangVersion>7.3</LangVersion>
|
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
cd $(dirname "$0")
|
|
||||||
../sh/quik_build_native.sh .
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using Quik.Media.Color;
|
|
||||||
|
|
||||||
namespace Quik.Media.Stb
|
|
||||||
{
|
|
||||||
public class QFontStbtt : QFont
|
|
||||||
{
|
|
||||||
public override FontInfo Info => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public QFontStbtt(Stream source)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public override QGlyphMetrics[] GetMetricsForPage(int codepage)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override QGlyphMetrics GetMetricsForRune(int rune)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool HasRune(int rune)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override QImage RenderPage(int codepage, float resolution, bool sdf)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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.StbTrueType\Quik.StbTrueType.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using OpenTK.Windowing.Desktop;
|
|
||||||
using OpenTK.Windowing.GraphicsLibraryFramework;
|
|
||||||
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 IQuikPort CreatePort()
|
|
||||||
{
|
|
||||||
NativeWindow window = new NativeWindow(DefaultSettings);
|
|
||||||
OpenTKPort port = new OpenTKPort(window);
|
|
||||||
_ports.Add(port);
|
|
||||||
|
|
||||||
if (!IsGLInitialized)
|
|
||||||
{
|
|
||||||
window.Context.MakeCurrent();
|
|
||||||
GL.LoadBindings((string proc) => GLFW.GetProcAddress(proc));
|
|
||||||
IsGLInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
|
||||||
|
|
||||||
project(quik_stbi LANGUAGES C VERSION 1.0)
|
|
||||||
|
|
||||||
add_compile_options(-static-libgcc)
|
|
||||||
|
|
||||||
add_library(stbi SHARED "quik_stbi.c")
|
|
||||||
target_include_directories(stbi PRIVATE "../lib")
|
|
||||||
install(
|
|
||||||
TARGETS stbi
|
|
||||||
RUNTIME DESTINATION .
|
|
||||||
LIBRARY DESTINATION .)
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Quik.Stb.Image
|
|
||||||
{
|
|
||||||
[AttributeUsage(System.AttributeTargets.All, Inherited = false, AllowMultiple = true)]
|
|
||||||
internal sealed class NativeTypeNameAttribute : System.Attribute
|
|
||||||
{
|
|
||||||
public NativeTypeNameAttribute(string typename)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
|
||||||
<Nullable>disable</Nullable>
|
|
||||||
<LangVersion>7.3</LangVersion>
|
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
|
||||||
<RuntimeIdentifiers>linux-arm;linux-arm64;linux-x64;win-x86;win-x64</RuntimeIdentifiers>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<!-- Nuget Properties. -->
|
|
||||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
|
||||||
<PackageId>Quik.StbImage</PackageId>
|
|
||||||
<Version>1.0.0</Version>
|
|
||||||
<Authors>STBI Authors, H. Utku Maden</Authors>
|
|
||||||
<Description>
|
|
||||||
A C# wrapper for the ubiquitous Stb Image library.
|
|
||||||
</Description>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="runtimes/**">
|
|
||||||
<PackagePath>runtimes</PackagePath>
|
|
||||||
<Pack>true</Pack>
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Quik.Stb
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A class that encompasses all features of stb_image.h in a safe way.
|
|
||||||
/// </summary>
|
|
||||||
public unsafe class StbImage : IDisposable
|
|
||||||
{
|
|
||||||
private bool isDisposed = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pointer to the image.
|
|
||||||
/// </summary>
|
|
||||||
public IntPtr ImagePointer { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Width of the image.
|
|
||||||
/// </summary>
|
|
||||||
public int Width { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Height of the image.
|
|
||||||
/// </summary>
|
|
||||||
/// <value></value>
|
|
||||||
public int Height { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Internal image format.
|
|
||||||
/// </summary>
|
|
||||||
public StbiImageFormat Format { get; }
|
|
||||||
public bool IsFloat { get; }
|
|
||||||
|
|
||||||
private StbImage(IntPtr image, int x, int y, StbiImageFormat format, bool isFloat)
|
|
||||||
{
|
|
||||||
ImagePointer = image;
|
|
||||||
Width = x;
|
|
||||||
Height = y;
|
|
||||||
Format = format;
|
|
||||||
IsFloat = isFloat;
|
|
||||||
}
|
|
||||||
|
|
||||||
~StbImage()
|
|
||||||
{
|
|
||||||
Dispose(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() => Dispose(true);
|
|
||||||
|
|
||||||
private void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (isDisposed) return;
|
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
Stbi.image_free(ImagePointer.ToPointer());
|
|
||||||
isDisposed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set to flip the y-axis of loaded images on load.
|
|
||||||
/// </summary>
|
|
||||||
public static bool FlipVerticallyOnLoad { set => Stbi.set_flip_vertically_on_load(1); }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set to unpremultiply images on load.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// According to the stb_image documentation, only iPhone PNG images
|
|
||||||
/// can come with premultiplied alpha.
|
|
||||||
/// </remarks>
|
|
||||||
public static bool UnpremultiplyOnLoad { set => Stbi.set_unpremultiply_on_load(1); }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Try loading an image, without raising exceptions.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="image">The resulting image.</param>
|
|
||||||
/// <param name="stream">Source stream.</param>
|
|
||||||
/// <param name="format">The desired image format.</param>
|
|
||||||
/// <returns>True on success.</returns>
|
|
||||||
public static bool TryLoad(out StbImage image, Stream stream, StbiImageFormat format = StbiImageFormat.Default, bool isFloat = false)
|
|
||||||
{
|
|
||||||
int x, y, iFormat;
|
|
||||||
StbiStreamWrapper wrapper = new StbiStreamWrapper(stream, true);
|
|
||||||
wrapper.CreateCallbacks(out stbi_io_callbacks cb);
|
|
||||||
|
|
||||||
stream.Position = 0;
|
|
||||||
IntPtr imagePtr;
|
|
||||||
if (isFloat)
|
|
||||||
{
|
|
||||||
imagePtr = (IntPtr)Stbi.loadf_from_callbacks(&cb, null, &x, &y, &iFormat, (int)format);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
imagePtr = (IntPtr)Stbi.load_from_callbacks(&cb, null, &x, &y, &iFormat, (int)format);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (imagePtr != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
image = new StbImage(imagePtr, x, y, (StbiImageFormat)iFormat, isFloat);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
image = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Load an image.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stream">The stream to load from.</param>
|
|
||||||
/// <param name="format">The desired image format.</param>
|
|
||||||
/// <returns>The image object.</returns>
|
|
||||||
public static StbImage Load(Stream stream, StbiImageFormat format = StbiImageFormat.Default, bool isFloat = false)
|
|
||||||
{
|
|
||||||
if (TryLoad(out StbImage image, stream, format, isFloat))
|
|
||||||
{
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
string reason = Marshal.PtrToStringUTF8((IntPtr)Stbi.failure_reason());
|
|
||||||
throw new Exception($"Failed to load image: {reason}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsLoadable(Stream stream)
|
|
||||||
{
|
|
||||||
int x, y, iFormat;
|
|
||||||
StbiStreamWrapper wrapper = new StbiStreamWrapper(stream, true);
|
|
||||||
wrapper.CreateCallbacks(out stbi_io_callbacks cb);
|
|
||||||
|
|
||||||
stream.Position = 0;
|
|
||||||
int result = Stbi.info_from_callbacks(&cb, null, &x, &y, &iFormat);
|
|
||||||
|
|
||||||
return result != 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Quik.Stb
|
|
||||||
{
|
|
||||||
public unsafe static partial class Stbi
|
|
||||||
{
|
|
||||||
private delegate void FailedAssertProc(byte *expression, byte *file, int line, byte *function);
|
|
||||||
|
|
||||||
private static readonly string[] LibraryNames = new string[]
|
|
||||||
{
|
|
||||||
//FIXME: This is wrong on so many levels, but, i need to do this
|
|
||||||
// in order to get a change of this running.
|
|
||||||
"runtimes/win-x64/native/libstbi.dll",
|
|
||||||
"runtimes/win-x86/native/libstbi.dll",
|
|
||||||
"runtimes/linux-arm/native/libstbi.so",
|
|
||||||
"runtimes/linux-arm64/native/libstbi.so",
|
|
||||||
"runtimes/linux-x64/native/libstbi.so",
|
|
||||||
"runtimes/native/libstbi.dylib",
|
|
||||||
"libstbi.dll",
|
|
||||||
"libstbi.so",
|
|
||||||
"libstbi.dylib",
|
|
||||||
};
|
|
||||||
|
|
||||||
static Stbi()
|
|
||||||
{
|
|
||||||
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), Resolver);
|
|
||||||
|
|
||||||
quik_stbi_failed_assert_store(Marshal.GetFunctionPointerForDelegate<FailedAssertProc>(FailedAssert));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IntPtr Resolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
|
|
||||||
{
|
|
||||||
if (libraryName != "stbi")
|
|
||||||
return IntPtr.Zero;
|
|
||||||
|
|
||||||
foreach (string name in LibraryNames)
|
|
||||||
{
|
|
||||||
if (NativeLibrary.TryLoad(name, assembly, searchPath, out IntPtr handle))
|
|
||||||
{
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NativeLibrary.Load(libraryName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void FailedAssert(byte *expression, byte *file, int line, byte *function)
|
|
||||||
{
|
|
||||||
string expr = expression == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)expression);
|
|
||||||
string f = file == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)file);
|
|
||||||
string func = function == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)function);
|
|
||||||
|
|
||||||
Exception ex = new Exception("Assert failed in native stbi code.");
|
|
||||||
ex.Data.Add("Expression", expr);
|
|
||||||
ex.Data.Add("File", f);
|
|
||||||
ex.Data.Add("Line", line);
|
|
||||||
ex.Data.Add("Function", func);
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Quik.Stb.Image;
|
|
||||||
|
|
||||||
namespace Quik.Stb
|
|
||||||
{
|
|
||||||
[NativeTypeName("unsigned int")]
|
|
||||||
public enum StbiEnum : uint
|
|
||||||
{
|
|
||||||
STBI_default = 0,
|
|
||||||
STBI_grey = 1,
|
|
||||||
STBI_grey_alpha = 2,
|
|
||||||
STBI_rgb = 3,
|
|
||||||
STBI_rgb_alpha = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial struct stbi_io_callbacks
|
|
||||||
{
|
|
||||||
[NativeTypeName("int (*)(void *, char *, int)")]
|
|
||||||
public IntPtr read;
|
|
||||||
|
|
||||||
[NativeTypeName("void (*)(void *, int)")]
|
|
||||||
public IntPtr skip;
|
|
||||||
|
|
||||||
[NativeTypeName("int (*)(void *)")]
|
|
||||||
public IntPtr eof;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static unsafe partial class Stbi
|
|
||||||
{
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
|
||||||
public static extern void quik_stbi_failed_assert_store([NativeTypeName("quik_failed_assert_cb_t")] IntPtr cb);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_from_memory", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("stbi_uc *")]
|
|
||||||
public static extern byte* load_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_from_callbacks", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("stbi_uc *")]
|
|
||||||
public static extern byte* load_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("stbi_uc *")]
|
|
||||||
public static extern byte* load([NativeTypeName("const char *")] sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_from_file", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("stbi_uc *")]
|
|
||||||
public static extern byte* load_from_file([NativeTypeName("FILE *")] void* f, int* x, int* y, int* channels_in_file, int desired_channels);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_gif_from_memory", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("stbi_uc *")]
|
|
||||||
public static extern byte* load_gif_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int** delays, int* x, int* y, int* z, int* comp, int req_comp);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_16_from_memory", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("stbi_us *")]
|
|
||||||
public static extern ushort* load_16_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_16_from_callbacks", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("stbi_us *")]
|
|
||||||
public static extern ushort* load_16_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_16", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("stbi_us *")]
|
|
||||||
public static extern ushort* load_16([NativeTypeName("const char *")] sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_from_file_16", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("stbi_us *")]
|
|
||||||
public static extern ushort* load_from_file_16([NativeTypeName("FILE *")] void* f, int* x, int* y, int* channels_in_file, int desired_channels);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_loadf_from_memory", ExactSpelling = true)]
|
|
||||||
public static extern float* loadf_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_loadf_from_callbacks", ExactSpelling = true)]
|
|
||||||
public static extern float* loadf_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_loadf", ExactSpelling = true)]
|
|
||||||
public static extern float* loadf([NativeTypeName("const char *")] sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_loadf_from_file", ExactSpelling = true)]
|
|
||||||
public static extern float* loadf_from_file([NativeTypeName("FILE *")] void* f, int* x, int* y, int* channels_in_file, int desired_channels);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_hdr_to_ldr_gamma", ExactSpelling = true)]
|
|
||||||
public static extern void hdr_to_ldr_gamma(float gamma);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_hdr_to_ldr_scale", ExactSpelling = true)]
|
|
||||||
public static extern void hdr_to_ldr_scale(float scale);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_ldr_to_hdr_gamma", ExactSpelling = true)]
|
|
||||||
public static extern void ldr_to_hdr_gamma(float gamma);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_ldr_to_hdr_scale", ExactSpelling = true)]
|
|
||||||
public static extern void ldr_to_hdr_scale(float scale);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_hdr_from_callbacks", ExactSpelling = true)]
|
|
||||||
public static extern int is_hdr_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_hdr_from_memory", ExactSpelling = true)]
|
|
||||||
public static extern int is_hdr_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_hdr", ExactSpelling = true)]
|
|
||||||
public static extern int is_hdr([NativeTypeName("const char *")] sbyte* filename);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_hdr_from_file", ExactSpelling = true)]
|
|
||||||
public static extern int is_hdr_from_file([NativeTypeName("FILE *")] void* f);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_failure_reason", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("const char *")]
|
|
||||||
public static extern sbyte* failure_reason();
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_image_free", ExactSpelling = true)]
|
|
||||||
public static extern void image_free(void* retval_from_stbi_load);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_info_from_memory", ExactSpelling = true)]
|
|
||||||
public static extern int info_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int* x, int* y, int* comp);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_info_from_callbacks", ExactSpelling = true)]
|
|
||||||
public static extern int info_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user, int* x, int* y, int* comp);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_16_bit_from_memory", ExactSpelling = true)]
|
|
||||||
public static extern int is_16_bit_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_16_bit_from_callbacks", ExactSpelling = true)]
|
|
||||||
public static extern int is_16_bit_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_info", ExactSpelling = true)]
|
|
||||||
public static extern int info([NativeTypeName("const char *")] sbyte* filename, int* x, int* y, int* comp);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_info_from_file", ExactSpelling = true)]
|
|
||||||
public static extern int info_from_file([NativeTypeName("FILE *")] void* f, int* x, int* y, int* comp);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_16_bit", ExactSpelling = true)]
|
|
||||||
public static extern int is_16_bit([NativeTypeName("const char *")] sbyte* filename);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_16_bit_from_file", ExactSpelling = true)]
|
|
||||||
public static extern int is_16_bit_from_file([NativeTypeName("FILE *")] void* f);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_set_unpremultiply_on_load", ExactSpelling = true)]
|
|
||||||
public static extern void set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_convert_iphone_png_to_rgb", ExactSpelling = true)]
|
|
||||||
public static extern void convert_iphone_png_to_rgb(int flag_true_if_should_convert);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_set_flip_vertically_on_load", ExactSpelling = true)]
|
|
||||||
public static extern void set_flip_vertically_on_load(int flag_true_if_should_flip);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_set_unpremultiply_on_load_thread", ExactSpelling = true)]
|
|
||||||
public static extern void set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_convert_iphone_png_to_rgb_thread", ExactSpelling = true)]
|
|
||||||
public static extern void convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_set_flip_vertically_on_load_thread", ExactSpelling = true)]
|
|
||||||
public static extern void set_flip_vertically_on_load_thread(int flag_true_if_should_flip);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_malloc_guesssize", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("char *")]
|
|
||||||
public static extern sbyte* zlib_decode_malloc_guesssize([NativeTypeName("const char *")] sbyte* buffer, int len, int initial_size, int* outlen);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_malloc_guesssize_headerflag", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("char *")]
|
|
||||||
public static extern sbyte* zlib_decode_malloc_guesssize_headerflag([NativeTypeName("const char *")] sbyte* buffer, int len, int initial_size, int* outlen, int parse_header);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_malloc", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("char *")]
|
|
||||||
public static extern sbyte* zlib_decode_malloc([NativeTypeName("const char *")] sbyte* buffer, int len, int* outlen);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_buffer", ExactSpelling = true)]
|
|
||||||
public static extern int zlib_decode_buffer([NativeTypeName("char *")] sbyte* obuffer, int olen, [NativeTypeName("const char *")] sbyte* ibuffer, int ilen);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_noheader_malloc", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("char *")]
|
|
||||||
public static extern sbyte* zlib_decode_noheader_malloc([NativeTypeName("const char *")] sbyte* buffer, int len, int* outlen);
|
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_noheader_buffer", ExactSpelling = true)]
|
|
||||||
public static extern int zlib_decode_noheader_buffer([NativeTypeName("char *")] sbyte* obuffer, int olen, [NativeTypeName("const char *")] sbyte* ibuffer, int ilen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
namespace Quik.Stb
|
|
||||||
{
|
|
||||||
public enum StbiImageFormat
|
|
||||||
{
|
|
||||||
Default = (int)StbiEnum.STBI_default,
|
|
||||||
Grey = (int)StbiEnum.STBI_grey,
|
|
||||||
GreyAlpha = (int)StbiEnum.STBI_grey_alpha,
|
|
||||||
Rgb = (int)StbiEnum.STBI_rgb,
|
|
||||||
Rgba = (int)StbiEnum.STBI_rgb_alpha
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Quik.Stb
|
|
||||||
{
|
|
||||||
public unsafe class StbiStreamWrapper : IDisposable
|
|
||||||
{
|
|
||||||
private Stream _stream;
|
|
||||||
private bool _keepOpen;
|
|
||||||
private bool _isDisposed;
|
|
||||||
|
|
||||||
private delegate int ReadProc(void *userdata, byte* buffer, int count);
|
|
||||||
private delegate void SkipProc(void *userdata, int count);
|
|
||||||
private delegate int Eof(void *userdata);
|
|
||||||
|
|
||||||
public StbiStreamWrapper(Stream stream, bool keepOpen = false)
|
|
||||||
{
|
|
||||||
if (stream == null) throw new ArgumentNullException(nameof(stream));
|
|
||||||
|
|
||||||
_stream = stream;
|
|
||||||
_keepOpen = keepOpen;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CreateCallbacks(out stbi_io_callbacks cb)
|
|
||||||
{
|
|
||||||
cb = default;
|
|
||||||
cb.read = Marshal.GetFunctionPointerForDelegate<ReadProc>(ReadCb);
|
|
||||||
cb.skip = Marshal.GetFunctionPointerForDelegate<SkipProc>(SkipCb);
|
|
||||||
cb.eof = Marshal.GetFunctionPointerForDelegate<Eof>(EofCb);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int ReadCb(void *userdata, byte* buffer, int count)
|
|
||||||
{
|
|
||||||
Span<byte> bytes = new Span<byte>(buffer, count);
|
|
||||||
return _stream.Read(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SkipCb(void *userdata, int count)
|
|
||||||
{
|
|
||||||
_stream.Seek(count, SeekOrigin.Current);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int EofCb(void *userdata)
|
|
||||||
{
|
|
||||||
if (!_stream.CanRead || _stream.Position == _stream.Length)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_isDisposed) return;
|
|
||||||
|
|
||||||
if (!_keepOpen) _stream.Dispose();
|
|
||||||
|
|
||||||
_isDisposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
cd $(dirname "$0")
|
|
||||||
../sh/quik_build_native.sh .
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
-x
|
|
||||||
c
|
|
||||||
-l
|
|
||||||
stbi
|
|
||||||
--config
|
|
||||||
compatible-codegen
|
|
||||||
single-file
|
|
||||||
exclude-fnptr-codegen
|
|
||||||
generate-aggressive-inlining
|
|
||||||
generate-setslastsystemerror-attribute
|
|
||||||
unix-types
|
|
||||||
--include-directory
|
|
||||||
../lib
|
|
||||||
--include-directory
|
|
||||||
../Quik.StbImage
|
|
||||||
--include-directory
|
|
||||||
/usr/lib/llvm-14/lib/clang/14.0.6/include
|
|
||||||
--file
|
|
||||||
../Quik.StbImage.redist/quik_stbi.h
|
|
||||||
../lib/stb/stb_image.h
|
|
||||||
--methodClassName
|
|
||||||
Stbi
|
|
||||||
--namespace
|
|
||||||
Quik.Stb
|
|
||||||
--output
|
|
||||||
Stbi.cs
|
|
||||||
--prefixStrip
|
|
||||||
stbi_
|
|
||||||
--with-type
|
|
||||||
FILE=void
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#include "quik_stbi.h"
|
|
||||||
|
|
||||||
QUIK_DEFINE_LIB(quik_stbi);
|
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION 1
|
|
||||||
#include "stb/stb_image.h"
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#ifndef _QUIK_STBI_H_
|
|
||||||
#define _QUIK_STBI_H_
|
|
||||||
|
|
||||||
#include "quik/quik_common.h"
|
|
||||||
|
|
||||||
QUIK_DECLARE_LIB(quik_stbi)
|
|
||||||
|
|
||||||
/* TODO: Change this declaration so we can export a DLL properly in windows. */
|
|
||||||
#define STBIDEF QEXTERN
|
|
||||||
|
|
||||||
#define STBI_ASSERT(EXPR) do { \
|
|
||||||
if (!(EXPR)) \
|
|
||||||
quik_stbi_failed_assert(#EXPR, __FILE__, __LINE__ - 2, __QUIK_FUNCTION__); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#include "stb/stb_image.h"
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
|
||||||
|
|
||||||
project(quik_stbtt LANGUAGES C VERSION 1.0)
|
|
||||||
|
|
||||||
add_compile_options(-static-libgcc)
|
|
||||||
|
|
||||||
add_library(stbtt SHARED "quik_stbtt.c")
|
|
||||||
target_include_directories(stbtt PRIVATE "../lib")
|
|
||||||
install(
|
|
||||||
TARGETS stbtt
|
|
||||||
RUNTIME DESTINATION .
|
|
||||||
LIBRARY DESTINATION .)
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Quik.Stb.TrueType
|
|
||||||
{
|
|
||||||
[AttributeUsage(System.AttributeTargets.All, Inherited = false, AllowMultiple = true)]
|
|
||||||
internal sealed class NativeTypeNameAttribute : System.Attribute
|
|
||||||
{
|
|
||||||
public NativeTypeNameAttribute(string typename)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
|
||||||
<Nullable>disable</Nullable>
|
|
||||||
<LangVersion>7.3</LangVersion>
|
|
||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Quik.StbTrueType.redist" Version="1.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="runtimes/**">
|
|
||||||
<PackagePath>runtimes</PackagePath>
|
|
||||||
<Pack>true</Pack>
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,355 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Quik.Stb
|
|
||||||
{
|
|
||||||
public unsafe class StbFont : IDisposable
|
|
||||||
{
|
|
||||||
IntPtr _buffer;
|
|
||||||
stbtt_fontinfo* _info;
|
|
||||||
List<stbtt_kerningentry> _kerningTable;
|
|
||||||
|
|
||||||
public IntPtr FontBuffer => _buffer;
|
|
||||||
public ref stbtt_fontinfo FontInfo => ref *_info;
|
|
||||||
|
|
||||||
public IReadOnlyList<stbtt_kerningentry> KerningTable
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_kerningTable != null)
|
|
||||||
return _kerningTable;
|
|
||||||
|
|
||||||
int count = Stbtt.GetKerningTableLength(_info);
|
|
||||||
|
|
||||||
if (count == 0)
|
|
||||||
{
|
|
||||||
return _kerningTable = new List<stbtt_kerningentry>();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stbtt_kerningentry[] array = new stbtt_kerningentry[count];
|
|
||||||
|
|
||||||
fixed (stbtt_kerningentry *ptr = array)
|
|
||||||
Stbtt.GetKerningTable(_info, ptr, count);
|
|
||||||
|
|
||||||
return _kerningTable = new List<stbtt_kerningentry>(array);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Ascend { get; }
|
|
||||||
public int Descend { get; }
|
|
||||||
public int VerticalLineGap { get; }
|
|
||||||
public int AscendOS2 { get; }
|
|
||||||
public int DescendOS2 { get; }
|
|
||||||
public int VerticalLineGapOS2 { get; }
|
|
||||||
public Box BoundingBox { get; }
|
|
||||||
|
|
||||||
private StbFont(IntPtr buffer, stbtt_fontinfo* info)
|
|
||||||
{
|
|
||||||
_buffer = buffer;
|
|
||||||
_info = info;
|
|
||||||
|
|
||||||
int a, b, c, d;
|
|
||||||
|
|
||||||
Stbtt.GetFontVMetrics(_info, &a, &b, &c);
|
|
||||||
Ascend = a;
|
|
||||||
Descend = b;
|
|
||||||
VerticalLineGap = c;
|
|
||||||
|
|
||||||
Stbtt.GetFontVMetricsOS2(_info, &a, &b, &c);
|
|
||||||
AscendOS2 = a;
|
|
||||||
DescendOS2 = b;
|
|
||||||
VerticalLineGapOS2 = c;
|
|
||||||
|
|
||||||
Stbtt.GetFontBoundingBox(_info, &a, &b, &c, &d);
|
|
||||||
BoundingBox = new Box(a, b, c, d);
|
|
||||||
}
|
|
||||||
~StbFont()
|
|
||||||
{
|
|
||||||
Dispose(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public int FindGlyphIndex(int codepoint)
|
|
||||||
{
|
|
||||||
return Stbtt.FindGlyphIndex(_info, codepoint);
|
|
||||||
}
|
|
||||||
public int FindGlyphIndex(Rune codepoint) => FindGlyphIndex(codepoint.Value);
|
|
||||||
|
|
||||||
public float ScaleForPixelHeight(float pixels)
|
|
||||||
{
|
|
||||||
return Stbtt.ScaleForPixelHeight(_info, pixels);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float ScaleForMappingEmToPixels(float pixels)
|
|
||||||
{
|
|
||||||
return Stbtt.ScaleForMappingEmToPixels(_info, pixels);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetCodepointHMetrics(int codepoint, out int advance, out int bearing)
|
|
||||||
{
|
|
||||||
int a, b;
|
|
||||||
|
|
||||||
Stbtt.GetCodepointHMetrics(_info, codepoint, &a, &b);
|
|
||||||
|
|
||||||
advance = a;
|
|
||||||
bearing = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetCodepointHMetrics(Rune codepoint, out int advance, out int bearing)
|
|
||||||
=> GetCodepointHMetrics(codepoint.Value, out advance, out bearing);
|
|
||||||
|
|
||||||
public int GetCodepointKernAdvance(int cp1, int cp2)
|
|
||||||
{
|
|
||||||
return Stbtt.GetCodepointKernAdvance(_info, cp1, cp2);
|
|
||||||
}
|
|
||||||
public int GetCodepointKernAdvance(Rune cp1, Rune cp2) => GetCodepointKernAdvance(cp1.Value, cp2.Value);
|
|
||||||
|
|
||||||
public int GetCodepointBox(int codepoint, out Box box)
|
|
||||||
{
|
|
||||||
int x0, y0;
|
|
||||||
int x1, y1;
|
|
||||||
int rval;
|
|
||||||
|
|
||||||
rval = Stbtt.GetCodepointBox(_info, codepoint, &x0, &y0, &x1, &y1);
|
|
||||||
|
|
||||||
box = new Box(x0, y0, x1, y1);
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetGlyphHMetrics(int glyph, out int advance, out int bearing)
|
|
||||||
{
|
|
||||||
int a, b;
|
|
||||||
|
|
||||||
Stbtt.GetGlyphHMetrics(_info, glyph, &a, &b);
|
|
||||||
|
|
||||||
advance = a;
|
|
||||||
bearing = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetGlyphKernAdvance(int gl1, int gl2)
|
|
||||||
{
|
|
||||||
return Stbtt.GetGlyphKernAdvance(_info, gl1, gl2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetGlyphBox(int glyph, out Box box)
|
|
||||||
{
|
|
||||||
int x0, y0;
|
|
||||||
int x1, y1;
|
|
||||||
int rval;
|
|
||||||
|
|
||||||
rval = Stbtt.GetGlyphBox(_info, glyph, &x0, &y0, &x1, &y1);
|
|
||||||
|
|
||||||
box = new Box(x0, y0, x1, y1);
|
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsGlyphEmpty(int glyph)
|
|
||||||
{
|
|
||||||
return Stbtt.IsGlyphEmpty(_info, glyph) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bitmap GetCodepointBitmap(float scaleX, float scaleY, int codepoint, out int offsetX, out int offsetY)
|
|
||||||
{
|
|
||||||
int w, h, x, y;
|
|
||||||
void* ptr = Stbtt.GetCodepointBitmap(_info, scaleX, scaleY, codepoint, &w, &h, &x, &y);
|
|
||||||
|
|
||||||
offsetX = x;
|
|
||||||
offsetY = y;
|
|
||||||
|
|
||||||
return new Bitmap((IntPtr)ptr, w, h, FreeBitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bitmap GetCodepointBitmap(float scaleX, float scaleY, Rune codepoint, out int offsetX, out int offsetY)
|
|
||||||
=> GetCodepointBitmap(scaleX, scaleY, codepoint.Value, out offsetX, out offsetY);
|
|
||||||
|
|
||||||
public Bitmap GetCodepointBitmapSubpixel(float scaleX, float scaleY, float shiftX, float shiftY, int codepoint, out int offsetX, out int offsetY)
|
|
||||||
{
|
|
||||||
int w, h, x, y;
|
|
||||||
void* ptr = Stbtt.GetCodepointBitmapSubpixel(_info, scaleX, scaleY, shiftX, shiftY, codepoint, &w, &h, &x, &y);
|
|
||||||
|
|
||||||
offsetX = x;
|
|
||||||
offsetY = y;
|
|
||||||
|
|
||||||
return new Bitmap((IntPtr)ptr, w, h, FreeBitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bitmap GetCodepointBitmapSubpixel(float scaleX, float scaleY, float shiftX, float shiftY, Rune codepoint, out int offsetX, out int offsetY)
|
|
||||||
=> GetCodepointBitmapSubpixel(scaleX, scaleY, shiftX, shiftY, codepoint.Value, out offsetX, out offsetY);
|
|
||||||
|
|
||||||
public Bitmap GetGlyphBitmap(float scaleX, float scaleY, int glyph, out int offsetX, out int offsetY)
|
|
||||||
{
|
|
||||||
int w, h, x, y;
|
|
||||||
void* ptr = Stbtt.GetGlyphBitmap(_info, scaleX, scaleY, glyph, &w, &h, &x, &y);
|
|
||||||
|
|
||||||
offsetX = x;
|
|
||||||
offsetY = y;
|
|
||||||
|
|
||||||
return new Bitmap((IntPtr)ptr, w, h, FreeBitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bitmap GetGlyphBitmapSubpixel(float scaleX, float scaleY, float shiftX, float shiftY, int glyph, out int offsetX, out int offsetY)
|
|
||||||
{
|
|
||||||
int w, h, x, y;
|
|
||||||
void* ptr = Stbtt.GetGlyphBitmapSubpixel(_info, scaleX, scaleY, shiftX, shiftY, glyph, &w, &h, &x, &y);
|
|
||||||
|
|
||||||
offsetX = x;
|
|
||||||
offsetY = y;
|
|
||||||
|
|
||||||
return new Bitmap((IntPtr)ptr, w, h, FreeBitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bitmap GetGlyphSdf(float scale, int glyph, int padding, byte edgeValue, float pixelDistScale, out int offsetX, out int offsetY)
|
|
||||||
{
|
|
||||||
int w, h, x, y;
|
|
||||||
void *ptr = Stbtt.GetGlyphSDF(_info, scale, glyph, padding, edgeValue, pixelDistScale, &w, &h, &x, &y);
|
|
||||||
|
|
||||||
offsetX = x;
|
|
||||||
offsetY = y;
|
|
||||||
|
|
||||||
return new Bitmap((IntPtr)ptr, w, h, FreeSdf);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bitmap GetCodepointSdf(float scale, int codepoint, int padding, byte edgeValue, float pixelDistScale, out int offsetX, out int offsetY)
|
|
||||||
{
|
|
||||||
int w, h, x, y;
|
|
||||||
void *ptr = Stbtt.GetCodepointSDF(_info, scale, codepoint, padding, edgeValue, pixelDistScale, &w, &h, &x, &y);
|
|
||||||
|
|
||||||
offsetX = x;
|
|
||||||
offsetY = y;
|
|
||||||
|
|
||||||
return new Bitmap((IntPtr)ptr, w, h, FreeSdf);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bitmap GetCodepointSdf(float scale, Rune codepoint, int padding, byte edgeValue, float pixelDistScale, out int offsetX, out int offsetY)
|
|
||||||
=> GetCodepointSdf(scale, codepoint.Value, padding, edgeValue, pixelDistScale, out offsetX, out offsetY);
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isDisposed = false;
|
|
||||||
private void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (isDisposed) return;
|
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Marshal.FreeHGlobal(_buffer);
|
|
||||||
Marshal.FreeHGlobal((IntPtr)_info);
|
|
||||||
isDisposed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TryLoad(Stream stream, out StbFont font)
|
|
||||||
{
|
|
||||||
byte* buffer = (byte*)Marshal.AllocHGlobal((int)stream.Length);
|
|
||||||
stbtt_fontinfo* fontInfo = (stbtt_fontinfo*)Marshal.AllocHGlobal(sizeof(stbtt_fontinfo));
|
|
||||||
|
|
||||||
stream.Read(new Span<byte>(buffer, (int)stream.Length));
|
|
||||||
|
|
||||||
int nfont = Stbtt.GetNumberOfFonts(buffer);
|
|
||||||
|
|
||||||
if (nfont == 0)
|
|
||||||
{
|
|
||||||
font = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int offset = Stbtt.GetFontOffsetForIndex(buffer, 0);
|
|
||||||
|
|
||||||
if (Stbtt.InitFont(fontInfo, (byte*)buffer, offset) == 0)
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal((IntPtr)buffer);
|
|
||||||
Marshal.FreeHGlobal((IntPtr)fontInfo);
|
|
||||||
|
|
||||||
font = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
font = new StbFont((IntPtr)buffer, fontInfo);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static StbFont Load(Stream stream)
|
|
||||||
{
|
|
||||||
if (TryLoad(stream, out StbFont font))
|
|
||||||
{
|
|
||||||
return font;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Exception("Could not load the font.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void FreeBitmap(IntPtr buffer)
|
|
||||||
{
|
|
||||||
Stbtt.FreeBitmap((byte*)buffer, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void FreeSdf(IntPtr buffer)
|
|
||||||
{
|
|
||||||
Stbtt.FreeSDF((byte*)buffer, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Box
|
|
||||||
{
|
|
||||||
public int X0;
|
|
||||||
public int Y0;
|
|
||||||
public int X1;
|
|
||||||
public int Y1;
|
|
||||||
|
|
||||||
public Box(int x0, int y0, int x1, int y1)
|
|
||||||
{
|
|
||||||
X0 = x0; Y0 = y0;
|
|
||||||
X1 = x1; Y1 = y1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Bitmap : IDisposable
|
|
||||||
{
|
|
||||||
public IntPtr Buffer { get; }
|
|
||||||
public int Width { get; }
|
|
||||||
public int Height { get; }
|
|
||||||
|
|
||||||
private readonly Action<IntPtr> Destroy;
|
|
||||||
|
|
||||||
public Bitmap(IntPtr buffer, int width, int height, Action<IntPtr> destroy)
|
|
||||||
{
|
|
||||||
Buffer = buffer;
|
|
||||||
Width = width;
|
|
||||||
Height = height;
|
|
||||||
Destroy = destroy;
|
|
||||||
}
|
|
||||||
~Bitmap()
|
|
||||||
{
|
|
||||||
Dispose(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() => Dispose(true);
|
|
||||||
|
|
||||||
private bool isDiposed = false;
|
|
||||||
public void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (isDiposed) return;
|
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
Destroy(Buffer);
|
|
||||||
isDiposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Quik.Stb
|
|
||||||
{
|
|
||||||
public unsafe static partial class Stbtt
|
|
||||||
{
|
|
||||||
private delegate void FailedAssertProc(byte *expression, byte *file, int line, byte *function);
|
|
||||||
|
|
||||||
private static readonly string[] LibraryNames = new string[]
|
|
||||||
{
|
|
||||||
//FIXME: This is wrong on so many levels, but, i need to do this
|
|
||||||
// in order to get a change of this running.
|
|
||||||
"runtimes/win-x64/native/libstbtt.dll",
|
|
||||||
"runtimes/win-x86/native/libstbtt.dll",
|
|
||||||
"runtimes/linux-arm/native/libstbtt.so",
|
|
||||||
"runtimes/linux-arm64/native/libstbtt.so",
|
|
||||||
"runtimes/linux-x64/native/libstbtt.so",
|
|
||||||
"runtimes/native/libstbtt.dylib",
|
|
||||||
"libstbtt.dll",
|
|
||||||
"libstbtt.so",
|
|
||||||
"libstbtt.dylib",
|
|
||||||
};
|
|
||||||
|
|
||||||
static Stbtt()
|
|
||||||
{
|
|
||||||
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), Resolver);
|
|
||||||
|
|
||||||
quik_stbtt_failed_assert_store(Marshal.GetFunctionPointerForDelegate<FailedAssertProc>(FailedAssert));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IntPtr Resolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
|
|
||||||
{
|
|
||||||
if (libraryName != "stbtt")
|
|
||||||
return IntPtr.Zero;
|
|
||||||
|
|
||||||
foreach (string name in LibraryNames)
|
|
||||||
{
|
|
||||||
if (NativeLibrary.TryLoad(name, assembly, searchPath, out IntPtr handle))
|
|
||||||
{
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NativeLibrary.Load(libraryName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void FailedAssert(byte *expression, byte *file, int line, byte *function)
|
|
||||||
{
|
|
||||||
string expr = expression == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)expression);
|
|
||||||
string f = file == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)file);
|
|
||||||
string func = function == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)function);
|
|
||||||
|
|
||||||
Exception ex =
|
|
||||||
new Exception($"Assert failed in native stbtt code. ({System.IO.Path.GetFileName(f)}:{line})");
|
|
||||||
ex.Data.Add("Expression", expr);
|
|
||||||
ex.Data.Add("File", f);
|
|
||||||
ex.Data.Add("Line", line);
|
|
||||||
ex.Data.Add("Function", func);
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,495 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Quik.Stb.TrueType;
|
|
||||||
|
|
||||||
namespace Quik.Stb
|
|
||||||
{
|
|
||||||
public unsafe partial struct stbtt__buf
|
|
||||||
{
|
|
||||||
[NativeTypeName("unsigned char *")]
|
|
||||||
public byte* data;
|
|
||||||
|
|
||||||
public int cursor;
|
|
||||||
|
|
||||||
public int size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial struct stbtt_bakedchar
|
|
||||||
{
|
|
||||||
[NativeTypeName("unsigned short")]
|
|
||||||
public ushort x0;
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned short")]
|
|
||||||
public ushort y0;
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned short")]
|
|
||||||
public ushort x1;
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned short")]
|
|
||||||
public ushort y1;
|
|
||||||
|
|
||||||
public float xoff;
|
|
||||||
|
|
||||||
public float yoff;
|
|
||||||
|
|
||||||
public float xadvance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial struct stbtt_aligned_quad
|
|
||||||
{
|
|
||||||
public float x0;
|
|
||||||
|
|
||||||
public float y0;
|
|
||||||
|
|
||||||
public float s0;
|
|
||||||
|
|
||||||
public float t0;
|
|
||||||
|
|
||||||
public float x1;
|
|
||||||
|
|
||||||
public float y1;
|
|
||||||
|
|
||||||
public float s1;
|
|
||||||
|
|
||||||
public float t1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial struct stbtt_packedchar
|
|
||||||
{
|
|
||||||
[NativeTypeName("unsigned short")]
|
|
||||||
public ushort x0;
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned short")]
|
|
||||||
public ushort y0;
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned short")]
|
|
||||||
public ushort x1;
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned short")]
|
|
||||||
public ushort y1;
|
|
||||||
|
|
||||||
public float xoff;
|
|
||||||
|
|
||||||
public float yoff;
|
|
||||||
|
|
||||||
public float xadvance;
|
|
||||||
|
|
||||||
public float xoff2;
|
|
||||||
|
|
||||||
public float yoff2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial struct stbrp_rect
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe partial struct stbtt_pack_range
|
|
||||||
{
|
|
||||||
public float font_size;
|
|
||||||
|
|
||||||
public int first_unicode_codepoint_in_range;
|
|
||||||
|
|
||||||
public int* array_of_unicode_codepoints;
|
|
||||||
|
|
||||||
public int num_chars;
|
|
||||||
|
|
||||||
public stbtt_packedchar* chardata_for_range;
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned char")]
|
|
||||||
public byte h_oversample;
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned char")]
|
|
||||||
public byte v_oversample;
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe partial struct stbtt_pack_context
|
|
||||||
{
|
|
||||||
public void* user_allocator_context;
|
|
||||||
|
|
||||||
public void* pack_info;
|
|
||||||
|
|
||||||
public int width;
|
|
||||||
|
|
||||||
public int height;
|
|
||||||
|
|
||||||
public int stride_in_bytes;
|
|
||||||
|
|
||||||
public int padding;
|
|
||||||
|
|
||||||
public int skip_missing;
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned int")]
|
|
||||||
public uint h_oversample;
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned int")]
|
|
||||||
public uint v_oversample;
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned char *")]
|
|
||||||
public byte* pixels;
|
|
||||||
|
|
||||||
public void* nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe partial struct stbtt_fontinfo
|
|
||||||
{
|
|
||||||
public void* userdata;
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned char *")]
|
|
||||||
public byte* data;
|
|
||||||
|
|
||||||
public int fontstart;
|
|
||||||
|
|
||||||
public int numGlyphs;
|
|
||||||
|
|
||||||
public int loca;
|
|
||||||
|
|
||||||
public int head;
|
|
||||||
|
|
||||||
public int glyf;
|
|
||||||
|
|
||||||
public int hhea;
|
|
||||||
|
|
||||||
public int hmtx;
|
|
||||||
|
|
||||||
public int kern;
|
|
||||||
|
|
||||||
public int gpos;
|
|
||||||
|
|
||||||
public int svg;
|
|
||||||
|
|
||||||
public int index_map;
|
|
||||||
|
|
||||||
public int indexToLocFormat;
|
|
||||||
|
|
||||||
public stbtt__buf cff;
|
|
||||||
|
|
||||||
public stbtt__buf charstrings;
|
|
||||||
|
|
||||||
public stbtt__buf gsubrs;
|
|
||||||
|
|
||||||
public stbtt__buf subrs;
|
|
||||||
|
|
||||||
public stbtt__buf fontdicts;
|
|
||||||
|
|
||||||
public stbtt__buf fdselect;
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial struct stbtt_kerningentry
|
|
||||||
{
|
|
||||||
public int glyph1;
|
|
||||||
|
|
||||||
public int glyph2;
|
|
||||||
|
|
||||||
public int advance;
|
|
||||||
}
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned int")]
|
|
||||||
public enum StbttV : uint
|
|
||||||
{
|
|
||||||
STBTT_vmove = 1,
|
|
||||||
STBTT_vline,
|
|
||||||
STBTT_vcurve,
|
|
||||||
STBTT_vcubic,
|
|
||||||
}
|
|
||||||
|
|
||||||
public partial struct stbtt_vertex
|
|
||||||
{
|
|
||||||
public short x;
|
|
||||||
|
|
||||||
public short y;
|
|
||||||
|
|
||||||
public short cx;
|
|
||||||
|
|
||||||
public short cy;
|
|
||||||
|
|
||||||
public short cx1;
|
|
||||||
|
|
||||||
public short cy1;
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned char")]
|
|
||||||
public byte type;
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned char")]
|
|
||||||
public byte padding;
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe partial struct stbtt__bitmap
|
|
||||||
{
|
|
||||||
public int w;
|
|
||||||
|
|
||||||
public int h;
|
|
||||||
|
|
||||||
public int stride;
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned char *")]
|
|
||||||
public byte* pixels;
|
|
||||||
}
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned int")]
|
|
||||||
public enum StbttPlatform : uint
|
|
||||||
{
|
|
||||||
STBTT_PLATFORM_ID_UNICODE = 0,
|
|
||||||
STBTT_PLATFORM_ID_MAC = 1,
|
|
||||||
STBTT_PLATFORM_ID_ISO = 2,
|
|
||||||
STBTT_PLATFORM_ID_MICROSOFT = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned int")]
|
|
||||||
public enum StbttUnicode : uint
|
|
||||||
{
|
|
||||||
STBTT_UNICODE_EID_UNICODE_1_0 = 0,
|
|
||||||
STBTT_UNICODE_EID_UNICODE_1_1 = 1,
|
|
||||||
STBTT_UNICODE_EID_ISO_10646 = 2,
|
|
||||||
STBTT_UNICODE_EID_UNICODE_2_0_BMP = 3,
|
|
||||||
STBTT_UNICODE_EID_UNICODE_2_0_FULL = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned int")]
|
|
||||||
public enum StbttMs : uint
|
|
||||||
{
|
|
||||||
STBTT_MS_EID_SYMBOL = 0,
|
|
||||||
STBTT_MS_EID_UNICODE_BMP = 1,
|
|
||||||
STBTT_MS_EID_SHIFTJIS = 2,
|
|
||||||
STBTT_MS_EID_UNICODE_FULL = 10,
|
|
||||||
}
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned int")]
|
|
||||||
public enum StbttMac : uint
|
|
||||||
{
|
|
||||||
STBTT_MAC_EID_ROMAN = 0,
|
|
||||||
STBTT_MAC_EID_ARABIC = 4,
|
|
||||||
STBTT_MAC_EID_JAPANESE = 1,
|
|
||||||
STBTT_MAC_EID_HEBREW = 5,
|
|
||||||
STBTT_MAC_EID_CHINESE_TRAD = 2,
|
|
||||||
STBTT_MAC_EID_GREEK = 6,
|
|
||||||
STBTT_MAC_EID_KOREAN = 3,
|
|
||||||
STBTT_MAC_EID_RUSSIAN = 7,
|
|
||||||
}
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned int")]
|
|
||||||
public enum StbttMsLang : uint
|
|
||||||
{
|
|
||||||
STBTT_MS_LANG_ENGLISH = 0x0409,
|
|
||||||
STBTT_MS_LANG_ITALIAN = 0x0410,
|
|
||||||
STBTT_MS_LANG_CHINESE = 0x0804,
|
|
||||||
STBTT_MS_LANG_JAPANESE = 0x0411,
|
|
||||||
STBTT_MS_LANG_DUTCH = 0x0413,
|
|
||||||
STBTT_MS_LANG_KOREAN = 0x0412,
|
|
||||||
STBTT_MS_LANG_FRENCH = 0x040c,
|
|
||||||
STBTT_MS_LANG_RUSSIAN = 0x0419,
|
|
||||||
STBTT_MS_LANG_GERMAN = 0x0407,
|
|
||||||
STBTT_MS_LANG_SPANISH = 0x0409,
|
|
||||||
STBTT_MS_LANG_HEBREW = 0x040d,
|
|
||||||
STBTT_MS_LANG_SWEDISH = 0x041D,
|
|
||||||
}
|
|
||||||
|
|
||||||
[NativeTypeName("unsigned int")]
|
|
||||||
public enum StbttMacLang : uint
|
|
||||||
{
|
|
||||||
STBTT_MAC_LANG_ENGLISH = 0,
|
|
||||||
STBTT_MAC_LANG_JAPANESE = 11,
|
|
||||||
STBTT_MAC_LANG_ARABIC = 12,
|
|
||||||
STBTT_MAC_LANG_KOREAN = 23,
|
|
||||||
STBTT_MAC_LANG_DUTCH = 4,
|
|
||||||
STBTT_MAC_LANG_RUSSIAN = 32,
|
|
||||||
STBTT_MAC_LANG_FRENCH = 1,
|
|
||||||
STBTT_MAC_LANG_SPANISH = 6,
|
|
||||||
STBTT_MAC_LANG_GERMAN = 2,
|
|
||||||
STBTT_MAC_LANG_SWEDISH = 5,
|
|
||||||
STBTT_MAC_LANG_HEBREW = 10,
|
|
||||||
STBTT_MAC_LANG_CHINESE_SIMPLIFIED = 33,
|
|
||||||
STBTT_MAC_LANG_ITALIAN = 3,
|
|
||||||
STBTT_MAC_LANG_CHINESE_TRAD = 19,
|
|
||||||
}
|
|
||||||
|
|
||||||
public static unsafe partial class Stbtt
|
|
||||||
{
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
|
||||||
public static extern void quik_stbtt_failed_assert_store([NativeTypeName("quik_failed_assert_cb_t")] IntPtr cb);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_BakeFontBitmap", ExactSpelling = true)]
|
|
||||||
public static extern int BakeFontBitmap([NativeTypeName("const unsigned char *")] byte* data, int offset, float pixel_height, [NativeTypeName("unsigned char *")] byte* pixels, int pw, int ph, int first_char, int num_chars, stbtt_bakedchar* chardata);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetBakedQuad", ExactSpelling = true)]
|
|
||||||
public static extern void GetBakedQuad([NativeTypeName("const stbtt_bakedchar *")] stbtt_bakedchar* chardata, int pw, int ph, int char_index, float* xpos, float* ypos, stbtt_aligned_quad* q, int opengl_fillrule);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetScaledFontVMetrics", ExactSpelling = true)]
|
|
||||||
public static extern void GetScaledFontVMetrics([NativeTypeName("const unsigned char *")] byte* fontdata, int index, float size, float* ascent, float* descent, float* lineGap);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_PackBegin", ExactSpelling = true)]
|
|
||||||
public static extern int PackBegin(stbtt_pack_context* spc, [NativeTypeName("unsigned char *")] byte* pixels, int width, int height, int stride_in_bytes, int padding, void* alloc_context);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_PackEnd", ExactSpelling = true)]
|
|
||||||
public static extern void PackEnd(stbtt_pack_context* spc);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_PackFontRange", ExactSpelling = true)]
|
|
||||||
public static extern int PackFontRange(stbtt_pack_context* spc, [NativeTypeName("const unsigned char *")] byte* fontdata, int font_index, float font_size, int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar* chardata_for_range);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_PackFontRanges", ExactSpelling = true)]
|
|
||||||
public static extern int PackFontRanges(stbtt_pack_context* spc, [NativeTypeName("const unsigned char *")] byte* fontdata, int font_index, stbtt_pack_range* ranges, int num_ranges);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_PackSetOversampling", ExactSpelling = true)]
|
|
||||||
public static extern void PackSetOversampling(stbtt_pack_context* spc, [NativeTypeName("unsigned int")] uint h_oversample, [NativeTypeName("unsigned int")] uint v_oversample);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_PackSetSkipMissingCodepoints", ExactSpelling = true)]
|
|
||||||
public static extern void PackSetSkipMissingCodepoints(stbtt_pack_context* spc, int skip);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetPackedQuad", ExactSpelling = true)]
|
|
||||||
public static extern void GetPackedQuad([NativeTypeName("const stbtt_packedchar *")] stbtt_packedchar* chardata, int pw, int ph, int char_index, float* xpos, float* ypos, stbtt_aligned_quad* q, int align_to_integer);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_PackFontRangesGatherRects", ExactSpelling = true)]
|
|
||||||
public static extern int PackFontRangesGatherRects(stbtt_pack_context* spc, [NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, stbtt_pack_range* ranges, int num_ranges, stbrp_rect* rects);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_PackFontRangesPackRects", ExactSpelling = true)]
|
|
||||||
public static extern void PackFontRangesPackRects(stbtt_pack_context* spc, stbrp_rect* rects, int num_rects);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_PackFontRangesRenderIntoRects", ExactSpelling = true)]
|
|
||||||
public static extern int PackFontRangesRenderIntoRects(stbtt_pack_context* spc, [NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, stbtt_pack_range* ranges, int num_ranges, stbrp_rect* rects);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetNumberOfFonts", ExactSpelling = true)]
|
|
||||||
public static extern int GetNumberOfFonts([NativeTypeName("const unsigned char *")] byte* data);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetFontOffsetForIndex", ExactSpelling = true)]
|
|
||||||
public static extern int GetFontOffsetForIndex([NativeTypeName("const unsigned char *")] byte* data, int index);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_InitFont", ExactSpelling = true)]
|
|
||||||
public static extern int InitFont(stbtt_fontinfo* info, [NativeTypeName("const unsigned char *")] byte* data, int offset);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_FindGlyphIndex", ExactSpelling = true)]
|
|
||||||
public static extern int FindGlyphIndex([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, int unicode_codepoint);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_ScaleForPixelHeight", ExactSpelling = true)]
|
|
||||||
public static extern float ScaleForPixelHeight([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, float pixels);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_ScaleForMappingEmToPixels", ExactSpelling = true)]
|
|
||||||
public static extern float ScaleForMappingEmToPixels([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, float pixels);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetFontVMetrics", ExactSpelling = true)]
|
|
||||||
public static extern void GetFontVMetrics([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, int* ascent, int* descent, int* lineGap);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetFontVMetricsOS2", ExactSpelling = true)]
|
|
||||||
public static extern int GetFontVMetricsOS2([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, int* typoAscent, int* typoDescent, int* typoLineGap);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetFontBoundingBox", ExactSpelling = true)]
|
|
||||||
public static extern void GetFontBoundingBox([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, int* x0, int* y0, int* x1, int* y1);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetCodepointHMetrics", ExactSpelling = true)]
|
|
||||||
public static extern void GetCodepointHMetrics([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, int codepoint, int* advanceWidth, int* leftSideBearing);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetCodepointKernAdvance", ExactSpelling = true)]
|
|
||||||
public static extern int GetCodepointKernAdvance([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, int ch1, int ch2);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetCodepointBox", ExactSpelling = true)]
|
|
||||||
public static extern int GetCodepointBox([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, int codepoint, int* x0, int* y0, int* x1, int* y1);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetGlyphHMetrics", ExactSpelling = true)]
|
|
||||||
public static extern void GetGlyphHMetrics([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, int glyph_index, int* advanceWidth, int* leftSideBearing);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetGlyphKernAdvance", ExactSpelling = true)]
|
|
||||||
public static extern int GetGlyphKernAdvance([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, int glyph1, int glyph2);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetGlyphBox", ExactSpelling = true)]
|
|
||||||
public static extern int GetGlyphBox([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, int glyph_index, int* x0, int* y0, int* x1, int* y1);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetKerningTableLength", ExactSpelling = true)]
|
|
||||||
public static extern int GetKerningTableLength([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetKerningTable", ExactSpelling = true)]
|
|
||||||
public static extern int GetKerningTable([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, stbtt_kerningentry* table, int table_length);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_IsGlyphEmpty", ExactSpelling = true)]
|
|
||||||
public static extern int IsGlyphEmpty([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, int glyph_index);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetCodepointShape", ExactSpelling = true)]
|
|
||||||
public static extern int GetCodepointShape([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, int unicode_codepoint, stbtt_vertex** vertices);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetGlyphShape", ExactSpelling = true)]
|
|
||||||
public static extern int GetGlyphShape([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, int glyph_index, stbtt_vertex** vertices);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_FreeShape", ExactSpelling = true)]
|
|
||||||
public static extern void FreeShape([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, stbtt_vertex* vertices);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_FindSVGDoc", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("unsigned char *")]
|
|
||||||
public static extern byte* FindSVGDoc([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, int gl);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetCodepointSVG", ExactSpelling = true)]
|
|
||||||
public static extern int GetCodepointSVG([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, int unicode_codepoint, [NativeTypeName("const char **")] sbyte** svg);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetGlyphSVG", ExactSpelling = true)]
|
|
||||||
public static extern int GetGlyphSVG([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, int gl, [NativeTypeName("const char **")] sbyte** svg);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_FreeBitmap", ExactSpelling = true)]
|
|
||||||
public static extern void FreeBitmap([NativeTypeName("unsigned char *")] byte* bitmap, void* userdata);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetCodepointBitmap", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("unsigned char *")]
|
|
||||||
public static extern byte* GetCodepointBitmap([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, float scale_x, float scale_y, int codepoint, int* width, int* height, int* xoff, int* yoff);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetCodepointBitmapSubpixel", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("unsigned char *")]
|
|
||||||
public static extern byte* GetCodepointBitmapSubpixel([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int* width, int* height, int* xoff, int* yoff);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_MakeCodepointBitmap", ExactSpelling = true)]
|
|
||||||
public static extern void MakeCodepointBitmap([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, [NativeTypeName("unsigned char *")] byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_MakeCodepointBitmapSubpixel", ExactSpelling = true)]
|
|
||||||
public static extern void MakeCodepointBitmapSubpixel([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, [NativeTypeName("unsigned char *")] byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_MakeCodepointBitmapSubpixelPrefilter", ExactSpelling = true)]
|
|
||||||
public static extern void MakeCodepointBitmapSubpixelPrefilter([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, [NativeTypeName("unsigned char *")] byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float* sub_x, float* sub_y, int codepoint);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetCodepointBitmapBox", ExactSpelling = true)]
|
|
||||||
public static extern void GetCodepointBitmapBox([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* font, int codepoint, float scale_x, float scale_y, int* ix0, int* iy0, int* ix1, int* iy1);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetCodepointBitmapBoxSubpixel", ExactSpelling = true)]
|
|
||||||
public static extern void GetCodepointBitmapBoxSubpixel([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int* ix0, int* iy0, int* ix1, int* iy1);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetGlyphBitmap", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("unsigned char *")]
|
|
||||||
public static extern byte* GetGlyphBitmap([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, float scale_x, float scale_y, int glyph, int* width, int* height, int* xoff, int* yoff);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetGlyphBitmapSubpixel", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("unsigned char *")]
|
|
||||||
public static extern byte* GetGlyphBitmapSubpixel([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int* width, int* height, int* xoff, int* yoff);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_MakeGlyphBitmap", ExactSpelling = true)]
|
|
||||||
public static extern void MakeGlyphBitmap([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, [NativeTypeName("unsigned char *")] byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_MakeGlyphBitmapSubpixel", ExactSpelling = true)]
|
|
||||||
public static extern void MakeGlyphBitmapSubpixel([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, [NativeTypeName("unsigned char *")] byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_MakeGlyphBitmapSubpixelPrefilter", ExactSpelling = true)]
|
|
||||||
public static extern void MakeGlyphBitmapSubpixelPrefilter([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, [NativeTypeName("unsigned char *")] byte* output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float* sub_x, float* sub_y, int glyph);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetGlyphBitmapBox", ExactSpelling = true)]
|
|
||||||
public static extern void GetGlyphBitmapBox([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* font, int glyph, float scale_x, float scale_y, int* ix0, int* iy0, int* ix1, int* iy1);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetGlyphBitmapBoxSubpixel", ExactSpelling = true)]
|
|
||||||
public static extern void GetGlyphBitmapBoxSubpixel([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* font, int glyph, float scale_x, float scale_y, float shift_x, float shift_y, int* ix0, int* iy0, int* ix1, int* iy1);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_Rasterize", ExactSpelling = true)]
|
|
||||||
public static extern void Rasterize(stbtt__bitmap* result, float flatness_in_pixels, stbtt_vertex* vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void* userdata);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_FreeSDF", ExactSpelling = true)]
|
|
||||||
public static extern void FreeSDF([NativeTypeName("unsigned char *")] byte* bitmap, void* userdata);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetGlyphSDF", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("unsigned char *")]
|
|
||||||
public static extern byte* GetGlyphSDF([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, float scale, int glyph, int padding, [NativeTypeName("unsigned char")] byte onedge_value, float pixel_dist_scale, int* width, int* height, int* xoff, int* yoff);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetCodepointSDF", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("unsigned char *")]
|
|
||||||
public static extern byte* GetCodepointSDF([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* info, float scale, int codepoint, int padding, [NativeTypeName("unsigned char")] byte onedge_value, float pixel_dist_scale, int* width, int* height, int* xoff, int* yoff);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_FindMatchingFont", ExactSpelling = true)]
|
|
||||||
public static extern int FindMatchingFont([NativeTypeName("const unsigned char *")] byte* fontdata, [NativeTypeName("const char *")] sbyte* name, int flags);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_CompareUTF8toUTF16_bigendian", ExactSpelling = true)]
|
|
||||||
public static extern int CompareUTF8toUTF16_bigendian([NativeTypeName("const char *")] sbyte* s1, int len1, [NativeTypeName("const char *")] sbyte* s2, int len2);
|
|
||||||
|
|
||||||
[DllImport("stbtt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbtt_GetFontNameString", ExactSpelling = true)]
|
|
||||||
[return: NativeTypeName("const char *")]
|
|
||||||
public static extern sbyte* GetFontNameString([NativeTypeName("const stbtt_fontinfo *")] stbtt_fontinfo* font, int* length, int platformID, int encodingID, int languageID, int nameID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user