namespace Ghost.Nvtt; /// /// Wrapper around an nvtt surface set — a collection of faces and mip levels /// loaded from a DDS file or built programmatically. /// public sealed unsafe class NvttSurfaceSetHandle : IDisposable { private NvttSurfaceSet* _ptr; /// Raw pointer - use only when calling the native API directly. public NvttSurfaceSet* Ptr => _ptr; // ------------------------------------------------------------------------- // Construction / destruction // ------------------------------------------------------------------------- public NvttSurfaceSetHandle() => _ptr = Api.nvttCreateSurfaceSet(); public void Dispose() { if (_ptr != null) { Api.nvttDestroySurfaceSet(_ptr); _ptr = null; } } // ------------------------------------------------------------------------- // Read-only properties // ------------------------------------------------------------------------- /// Texture dimensionality stored in this set. public NvttTextureType TextureType { get { ThrowIfDisposed(); return Api.nvttSurfaceSetGetTextureType(_ptr); } } /// Number of faces (1 for 2-D / 3-D, 6 for cube maps). public int FaceCount { get { ThrowIfDisposed(); return Api.nvttSurfaceSetGetFaceCount(_ptr); } } /// Number of mip levels. public int MipmapCount { get { ThrowIfDisposed(); return Api.nvttSurfaceSetGetMipmapCount(_ptr); } } /// Width of the base (mip 0) image in pixels. public int Width { get { ThrowIfDisposed(); return Api.nvttSurfaceSetGetWidth(_ptr); } } /// Height of the base (mip 0) image in pixels. public int Height { get { ThrowIfDisposed(); return Api.nvttSurfaceSetGetHeight(_ptr); } } /// Depth of the base (mip 0) image (1 for 2-D textures). public int Depth { get { ThrowIfDisposed(); return Api.nvttSurfaceSetGetDepth(_ptr); } } // ------------------------------------------------------------------------- // Surface access // ------------------------------------------------------------------------- /// /// Returns the raw pointer for the given face /// and mip level. The pointer is owned by this surface set - do NOT dispose /// it. /// public NvttSurface* GetSurfacePtr(int faceId, int mipId, bool expectSigned = false) { ThrowIfDisposed(); return Api.nvttSurfaceSetGetSurface(_ptr, faceId, mipId, NvttInterop.ToNvtt(expectSigned)); } // ------------------------------------------------------------------------- // Load / Save // ------------------------------------------------------------------------- /// Resets the surface set to an empty state. public void Reset() { ThrowIfDisposed(); Api.nvttResetSurfaceSet(_ptr); } /// Loads from a DDS file. Returns false on failure. public bool LoadDDS(string fileName, bool forceNormal = false) { ThrowIfDisposed(); Span buf = stackalloc byte[NvttInterop._MAX_STACK_PATH]; var utf8 = NvttInterop.ToUtf8(fileName, buf); fixed (byte* p = utf8) { return NvttInterop.ToBool( Api.nvttSurfaceSetLoadDDS(_ptr, (sbyte*)p, NvttInterop.ToNvtt(forceNormal))); } } /// Loads from a managed byte array containing DDS data. Returns false on failure. public bool LoadDDSFromMemory(ReadOnlySpan data, bool forceNormal = false) { ThrowIfDisposed(); fixed (byte* p = data) { return NvttInterop.ToBool( Api.nvttSurfaceSetLoadDDSFromMemory(_ptr, p, (ulong)data.Length, NvttInterop.ToNvtt(forceNormal))); } } /// Saves a single face/mip as an image file. Returns false on failure. public bool SaveImage(string fileName, int faceId, int mipId) { ThrowIfDisposed(); Span buf = stackalloc byte[NvttInterop._MAX_STACK_PATH]; var utf8 = NvttInterop.ToUtf8(fileName, buf); fixed (byte* p = utf8) { return NvttInterop.ToBool( Api.nvttSurfaceSetSaveImage(_ptr, (sbyte*)p, faceId, mipId)); } } // ------------------------------------------------------------------------- private void ThrowIfDisposed() { if (_ptr == null) { throw new ObjectDisposedException(nameof(NvttSurfaceSetHandle)); } } }