Files
2026-05-21 08:18:52 -05:00

183 lines
5.6 KiB
C#

using System.Numerics;
using Hexa.NET.ImGui;
using Hexa.NET.OpenGL;
using SDL3;
namespace SDL3_TestingSuite.SDL3;
public sealed unsafe class OpenGLTexture : IDisposable
{
private readonly GL _gl;
public uint Handle { get; private set; }
public int Width { get; }
public int Height { get; }
public ImTextureRef ImGuiTexture => new ImTextureRef(null, (nint)Handle);
public Vector2 Size => new Vector2(Width, Height);
public string? FileName;
public string? FilePath;
public string? Extension;
private OpenGLTexture(GL gl, uint handle, int width, int height, string? path = null)
{
_gl = gl;
Handle = handle;
Width = width;
Height = height;
if (path == null) return;
FileName = Path.GetFileName(path);
FilePath = path;
Extension = Path.GetExtension(path);
}
public static OpenGLTexture FromFile(GL gl, string path)
{
nint surface = string.Equals(Path.GetExtension(path), ".bmp", StringComparison.OrdinalIgnoreCase)
? SDL.LoadBMP(path)
: Image.Load(path);
if (surface == nint.Zero)
{
throw new InvalidOperationException($"Failed to load image '{path}': {SDL.GetError()}");
}
try
{
return FromSurface(gl, surface, path);
}
finally
{
SDL.DestroySurface(surface);
}
}
public static OpenGLTexture FromSurface(GL gl, nint surface, string? path = null)
{
if (surface == nint.Zero)
{
throw new ArgumentException("Surface cannot be null.", nameof(surface));
}
SDL.PixelFormat uploadFormat = BitConverter.IsLittleEndian
? SDL.PixelFormat.ABGR8888
: SDL.PixelFormat.RGBA8888;
nint converted = SDL.ConvertSurface(surface, uploadFormat);
if (converted == nint.Zero)
{
throw new InvalidOperationException($"Failed to convert image surface to RGBA32: {SDL.GetError()}");
}
try
{
SDL.Surface* convertedSurface = (SDL.Surface*)converted;
bool locked = false;
if (SDL.MustLock(*convertedSurface))
{
if (!SDL.LockSurface(converted))
{
throw new InvalidOperationException($"Failed to lock image surface: {SDL.GetError()}");
}
locked = true;
}
try
{
uint texture = UploadRgba32(gl, convertedSurface);
return new OpenGLTexture(gl, texture, convertedSurface->Width, convertedSurface->Height, path);
}
finally
{
if (locked)
{
SDL.UnlockSurface(converted);
}
}
}
finally
{
SDL.DestroySurface(converted);
}
}
private static uint UploadRgba32(GL gl, SDL.Surface* surface)
{
uint texture = 0;
gl.GenTextures(1, ref texture);
gl.GetIntegerv(GLGetPName.TextureBinding2D, out int previousTexture);
gl.BindTexture(GLTextureTarget.Texture2D, texture);
gl.TexParameteri(GLTextureTarget.Texture2D, GLTextureParameterName.MinFilter, (int)GLTextureMinFilter.Linear);
gl.TexParameteri(GLTextureTarget.Texture2D, GLTextureParameterName.MagFilter, (int)GLTextureMagFilter.Linear);
gl.TexParameteri(GLTextureTarget.Texture2D, GLTextureParameterName.WrapS, (int)GLEnum.ClampToEdge);
gl.TexParameteri(GLTextureTarget.Texture2D, GLTextureParameterName.WrapT, (int)GLEnum.ClampToEdge);
gl.PixelStorei(GLPixelStoreParameter.UnpackAlignment, 1);
gl.PixelStorei(GLPixelStoreParameter.UnpackRowLength, surface->Pitch / 4);
gl.TexImage2D(
GLTextureTarget.Texture2D,
0,
GLInternalFormat.Rgba,
surface->Width,
surface->Height,
0,
GLPixelFormat.Rgba,
GLPixelType.UnsignedByte,
surface->Pixels);
gl.PixelStorei(GLPixelStoreParameter.UnpackRowLength, 0);
gl.BindTexture(GLTextureTarget.Texture2D, (uint)previousTexture);
return texture;
}
private static uint UploadRgba32FromPixels(GL gl, byte[] pixels, int width, int height)
{
uint texture = 0;
gl.GenTextures(1, ref texture);
gl.GetIntegerv(GLGetPName.TextureBinding2D, out int previousTexture);
gl.BindTexture(GLTextureTarget.Texture2D, texture);
gl.TexParameteri(GLTextureTarget.Texture2D, GLTextureParameterName.MinFilter, (int)GLTextureMinFilter.Linear);
gl.TexParameteri(GLTextureTarget.Texture2D, GLTextureParameterName.MagFilter, (int)GLTextureMagFilter.Linear);
gl.TexParameteri(GLTextureTarget.Texture2D, GLTextureParameterName.WrapS, (int)GLEnum.ClampToEdge);
gl.TexParameteri(GLTextureTarget.Texture2D, GLTextureParameterName.WrapT, (int)GLEnum.ClampToEdge);
gl.PixelStorei(GLPixelStoreParameter.UnpackAlignment, 1);
fixed (byte* ptr = &pixels[0])
{
gl.TexImage2D(
GLTextureTarget.Texture2D,
0,
GLInternalFormat.Rgba,
width,
height,
0,
GLPixelFormat.Rgba,
GLPixelType.UnsignedByte,
(nint)ptr);
}
gl.BindTexture(GLTextureTarget.Texture2D, (uint)previousTexture);
return texture;
}
public void Dispose()
{
if (Handle == 0)
{
return;
}
_gl.DeleteTexture(Handle);
Handle = 0;
}
}