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));
}
}
}