using System; namespace Quik.Media { public abstract class QImage : IDisposable { public abstract int Width { get; } public abstract int Height { get; } public abstract int Depth { get; } public abstract QImageFormat 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 QImageFormat Format { get; } public bool Premultiply { get; } public int MipLevel { get; } public static QImageLockOptions Default { get; } = new QImageLockOptions(QImageFormat.RgbaU8, true, 0); public QImageLockOptions(QImageFormat format, bool premultiply, int level) { Format = format; Premultiply = premultiply; MipLevel = level; } } public struct QImageLock { public QImageFormat Format { get; } public int Width { get; } public int Height { get; } public int Depth { get; } public IntPtr ImagePtr { get; } public QImageLock(QImageFormat 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); } } } }