265 lines
11 KiB
C#
265 lines
11 KiB
C#
using System.Numerics;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using BlurgText;
|
|
using Dashboard.Drawing;
|
|
using Dashboard.OpenGL;
|
|
using OpenTK.Graphics.OpenGL;
|
|
using OPENGL = OpenTK.Graphics.OpenGL;
|
|
|
|
namespace Dashboard.BlurgText.OpenGL
|
|
{
|
|
public class BlurgGLExtension : BlurgDcExtension
|
|
{
|
|
private readonly List<int> _textures = new List<int>();
|
|
private int _program = 0;
|
|
private int _transformsLocation = -1;
|
|
private int _atlasLocation = -1;
|
|
private int _borderWidthLocation = -1;
|
|
private int _borderColorLocation = -1;
|
|
private int _fillColorLocation = -1;
|
|
private int _vertexArray = 0;
|
|
|
|
public override Blurg Blurg { get; }
|
|
public bool SystemFontsEnabled { get; set; }
|
|
public bool IsDisposed { get; private set; } = false;
|
|
|
|
public override string DriverName => "BlurgText";
|
|
public override string DriverVendor => "Dashboard and BlurgText";
|
|
public override Version DriverVersion => new Version(1, 0);
|
|
|
|
private new GLDeviceContext Context => (GLDeviceContext)base.Context;
|
|
|
|
public BlurgGLExtension()
|
|
{
|
|
Blurg = new Blurg(AllocateTexture, UpdateTexture);
|
|
SystemFontsEnabled = Blurg.EnableSystemFonts();
|
|
}
|
|
|
|
~BlurgGLExtension()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
|
|
public BlurgFontProxy InternFont(IFont font)
|
|
{
|
|
BlurgTextExtension appExtension = Application.ExtensionRequire<BlurgTextExtension>();
|
|
if (font is FontInfo fontInfo)
|
|
{
|
|
return (BlurgFontProxy)appExtension.Load(fontInfo);
|
|
}
|
|
else if (font is BlurgFontProxy blurgFont)
|
|
{
|
|
if (blurgFont.Owner != Blurg)
|
|
{
|
|
if (blurgFont.Path == null)
|
|
return (BlurgFontProxy)appExtension.Load(new FontInfo(blurgFont.Family, blurgFont.Weight,
|
|
blurgFont.Slant,
|
|
blurgFont.Stretch));
|
|
else
|
|
return (BlurgFontProxy)appExtension.Load(blurgFont.Path);
|
|
}
|
|
else
|
|
{
|
|
return blurgFont;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new Exception("Unsupported font resource.");
|
|
}
|
|
}
|
|
|
|
private void UseProgram()
|
|
{
|
|
if (_program != 0)
|
|
{
|
|
GL.UseProgram(_program);
|
|
return;
|
|
}
|
|
|
|
Assembly self = typeof(BlurgGLExtension).Assembly;
|
|
|
|
using Stream vsource = self.GetManifestResourceStream("Dashboard.BlurgText.OpenGL.text.vert")!;
|
|
using Stream fsource = self.GetManifestResourceStream("Dashboard.BlurgText.OpenGL.text.frag")!;
|
|
int vs = ShaderUtil.CompileShader(ShaderType.VertexShader, vsource);
|
|
int fs = ShaderUtil.CompileShader(ShaderType.FragmentShader, fsource);
|
|
_program = ShaderUtil.LinkProgram(vs, fs, [
|
|
"a_v3Position",
|
|
"a_v2TexCoords",
|
|
]);
|
|
GL.DeleteShader(vs);
|
|
GL.DeleteShader(fs);
|
|
|
|
_transformsLocation = GL.GetUniformLocation(_program, "m4Transforms");
|
|
_atlasLocation = GL.GetUniformLocation(_program, "txAtlas");
|
|
_borderWidthLocation = GL.GetUniformLocation(_program, "fBorderWidth");
|
|
_borderColorLocation = GL.GetUniformLocation(_program, "v4BorderColor");
|
|
_fillColorLocation = GL.GetUniformLocation(_program, "v4FillColor");
|
|
|
|
GL.UseProgram(_program);
|
|
GL.Uniform1i(_atlasLocation, 0);
|
|
}
|
|
|
|
private void UpdateTexture(IntPtr texture, IntPtr buffer, int x, int y, int width, int height)
|
|
{
|
|
GL.BindTexture(TextureTarget.Texture2d, (int)texture);
|
|
GL.TexSubImage2D(TextureTarget.Texture2d, 0, x, y, width, height, OPENGL.PixelFormat.Rgba, PixelType.UnsignedByte, buffer);
|
|
// GL.TexSubImage2D(TextureTarget.Texture2d, 0, x, y, width, height, OPENGL.PixelFormat.Red, PixelType.Byte, buffer);
|
|
}
|
|
|
|
|
|
private IntPtr AllocateTexture(int width, int height)
|
|
{
|
|
int texture = GL.GenTexture();
|
|
|
|
GL.BindTexture(TextureTarget.Texture2d, texture);
|
|
GL.TexImage2D(TextureTarget.Texture2d, 0, InternalFormat.Rgba, width, height, 0, OPENGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
|
|
// GL.TexImage2D(TextureTarget.Texture2d, 0, InternalFormat.R8, width, height, 0, OPENGL.PixelFormat.Red, PixelType.Byte, IntPtr.Zero);
|
|
|
|
GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
|
GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
|
// GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleR, (int)TextureSwizzle.One);
|
|
// GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleG, (int)TextureSwizzle.One);
|
|
// GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleB, (int)TextureSwizzle.One);
|
|
// GL.TexParameteri(TextureTarget.Texture2d, TextureParameterName.TextureSwizzleA, (int)TextureSwizzle.Red);
|
|
|
|
_textures.Add(texture);
|
|
|
|
return texture;
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (IsDisposed)
|
|
return;
|
|
IsDisposed = true;
|
|
|
|
if (disposing)
|
|
{
|
|
GC.SuppressFinalize(this);
|
|
foreach (int texture in _textures)
|
|
{
|
|
Context.Collector.DeleteTexture(texture);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void DrawBlurgResult(BlurgResult result, Vector3 position)
|
|
{
|
|
if (result.Count == 0)
|
|
return;
|
|
|
|
Matrix4x4 view = Matrix4x4.CreateTranslation(position) * Context.ExtensionRequire<IDeviceContextBase>().Transforms;
|
|
|
|
List<DrawCall> drawCalls = new List<DrawCall>();
|
|
List<Vertex> vertices = new List<Vertex>();
|
|
List<ushort> indices = new List<ushort>();
|
|
int offset = 0;
|
|
int count = 0;
|
|
|
|
DrawCall prototype = default;
|
|
for (int i = 0; i < result.Count; i++)
|
|
{
|
|
BlurgRect rect = result[i];
|
|
|
|
int texture = (int)rect.UserData;
|
|
Vector4 fillColor = new Vector4(rect.Color.R / 255f, rect.Color.G / 255f, rect.Color.B / 255f,
|
|
rect.Color.A / 255f);
|
|
|
|
if (i == 0)
|
|
{
|
|
prototype = new DrawCall(0, 0, texture, fillColor);
|
|
}
|
|
else if (prototype.Texture != texture || prototype.FillColor != fillColor)
|
|
{
|
|
drawCalls.Add(prototype with { Count = count, Offset = offset });
|
|
prototype = new DrawCall(0, 0, texture, fillColor);
|
|
offset += count;
|
|
count = 0;
|
|
}
|
|
|
|
vertices.Add(new Vertex(rect.X, rect.Y, 0, rect.U0, rect.V0));
|
|
vertices.Add(new Vertex(rect.X + rect.Width, rect.Y, 0, rect.U1, rect.V0));
|
|
vertices.Add(new Vertex(rect.X + rect.Width, rect.Y + rect.Height, 0, rect.U1, rect.V1));
|
|
vertices.Add(new Vertex(rect.X, rect.Y + rect.Height, 0, rect.U0, rect.V1));
|
|
|
|
indices.Add((ushort)(vertices.Count - 4));
|
|
indices.Add((ushort)(vertices.Count - 3));
|
|
indices.Add((ushort)(vertices.Count - 2));
|
|
indices.Add((ushort)(vertices.Count - 4));
|
|
indices.Add((ushort)(vertices.Count - 2));
|
|
indices.Add((ushort)(vertices.Count - 1));
|
|
|
|
count += 6;
|
|
}
|
|
drawCalls.Add(prototype with { Count = count, Offset = offset });
|
|
|
|
if (_vertexArray == 0)
|
|
{
|
|
_vertexArray = GL.GenVertexArray();
|
|
}
|
|
GL.BindVertexArray(_vertexArray);
|
|
|
|
Span<int> buffers = stackalloc int[2];
|
|
GL.GenBuffers(2, buffers);
|
|
GL.BindBuffer(BufferTarget.ArrayBuffer, buffers[0]);
|
|
GL.BufferData(BufferTarget.ArrayBuffer, vertices.Count * Vertex.Size, (ReadOnlySpan<Vertex>)CollectionsMarshal.AsSpan(vertices), BufferUsage.StaticRead);
|
|
|
|
GL.BindBuffer(BufferTarget.ElementArrayBuffer, buffers[1]);
|
|
GL.BufferData(BufferTarget.ElementArrayBuffer, indices.Count * sizeof(ushort), (ReadOnlySpan<ushort>)CollectionsMarshal.AsSpan(indices), BufferUsage.StaticRead);
|
|
|
|
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, Vertex.Size, Vertex.PositionOffset);
|
|
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, Vertex.Size, Vertex.TexCoordOffset);
|
|
GL.EnableVertexAttribArray(0); GL.EnableVertexAttribArray(1);
|
|
|
|
UseProgram();
|
|
|
|
GL.UniformMatrix4f(_transformsLocation, 1, true, in view);
|
|
GL.ActiveTexture(TextureUnit.Texture0);
|
|
|
|
foreach (DrawCall call in drawCalls)
|
|
{
|
|
GL.BindTexture(TextureTarget.Texture2d, call.Texture);
|
|
GL.Uniform4f(_fillColorLocation, call.FillColor.X, call.FillColor.Y, call.FillColor.Z,
|
|
call.FillColor.W);
|
|
GL.DrawElements(PrimitiveType.Triangles, call.Count, DrawElementsType.UnsignedShort, call.Offset);
|
|
}
|
|
|
|
GL.DeleteBuffers(2, buffers);
|
|
}
|
|
|
|
public override void Dispose()
|
|
{
|
|
Dispose(true);
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Explicit, Size = Size)]
|
|
private struct Vertex(Vector3 position, Vector2 texCoord)
|
|
{
|
|
[FieldOffset(PositionOffset)]
|
|
public Vector3 Position = position;
|
|
|
|
[FieldOffset(TexCoordOffset)]
|
|
public Vector2 TexCoord = texCoord;
|
|
|
|
public Vertex(float x, float y, float z, float u, float v)
|
|
: this(new Vector3(x, y, z), new Vector2(u, v))
|
|
{
|
|
}
|
|
|
|
public const int Size = 8 * sizeof(float);
|
|
public const int PositionOffset = 0 * sizeof(float);
|
|
public const int TexCoordOffset = 4 * sizeof(float);
|
|
}
|
|
|
|
private struct DrawCall(int offset, int count, int texture, Vector4 fillColor)
|
|
{
|
|
public int Offset = offset;
|
|
public int Count = count;
|
|
public int Texture = texture;
|
|
public Vector4 FillColor = fillColor;
|
|
}
|
|
}
|
|
}
|