Add image buffers and color conversion.
This commit is contained in:
parent
995943d83b
commit
1676184118
59
Quik/Media/Color/FormatConvert.cs
Normal file
59
Quik/Media/Color/FormatConvert.cs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Quik.Media.Color
|
||||||
|
{
|
||||||
|
public static class FormatConvert
|
||||||
|
{
|
||||||
|
public static void Convert(QImageLock dst, QImageLock src)
|
||||||
|
{
|
||||||
|
if (dst.Format.IsU8() && src.Format.IsU8())
|
||||||
|
{
|
||||||
|
LockIO dstIO = new LockIO(dst);
|
||||||
|
LockIO srcIO = new LockIO(src);
|
||||||
|
|
||||||
|
int count = dst.Width * dst.Height * dst.Depth;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
dstIO[i] = srcIO[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dst.Format.IsU8() && src.Format.IsFloat())
|
||||||
|
{
|
||||||
|
LockIO dstIO = new LockIO(dst);
|
||||||
|
LockIOF srcIO = new LockIOF(src);
|
||||||
|
|
||||||
|
int count = dst.Width * dst.Height * dst.Depth;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
dstIO[i] = (QColor)srcIO[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dst.Format.IsFloat() && src.Format.IsU8())
|
||||||
|
{
|
||||||
|
LockIOF dstIO = new LockIOF(dst);
|
||||||
|
LockIO srcIO = new LockIO(src);
|
||||||
|
|
||||||
|
int count = dst.Width * dst.Height * dst.Depth;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
dstIO[i] = (QColorF)srcIO[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dst.Format.IsFloat() && src.Format.IsFloat())
|
||||||
|
{
|
||||||
|
LockIOF dstIO = new LockIOF(dst);
|
||||||
|
LockIOF srcIO = new LockIOF(src);
|
||||||
|
|
||||||
|
int count = dst.Width * dst.Height * dst.Depth;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
dstIO[i] = srcIO[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Congratulations you have broken image formats!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
Quik/Media/Color/ImageBuffer.cs
Normal file
60
Quik/Media/Color/ImageBuffer.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Quik.Media.Color
|
||||||
|
{
|
||||||
|
public class ImageBuffer : IDisposable
|
||||||
|
{
|
||||||
|
private byte[] buffer;
|
||||||
|
GCHandle handle;
|
||||||
|
|
||||||
|
public QImageFormat Format { get; }
|
||||||
|
public int Width { get; }
|
||||||
|
public int Height { get; }
|
||||||
|
public int Depth { get; }
|
||||||
|
|
||||||
|
public ImageBuffer(QImageFormat format, int width, int height, int depth = 1)
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
Width = width;
|
||||||
|
Height = height;
|
||||||
|
Depth = depth;
|
||||||
|
|
||||||
|
buffer = new byte[width * height * depth];
|
||||||
|
}
|
||||||
|
~ImageBuffer()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public QImageLock Lock()
|
||||||
|
{
|
||||||
|
handle.Free();
|
||||||
|
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||||
|
IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
|
||||||
|
return new QImageLock(Format, Width, Height, Depth, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unlock()
|
||||||
|
{
|
||||||
|
handle.Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool isDiposed = false;
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (isDiposed) return;
|
||||||
|
|
||||||
|
buffer = null;
|
||||||
|
handle.Free();
|
||||||
|
|
||||||
|
isDiposed = true;
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
150
Quik/Media/Color/LockIO.cs
Normal file
150
Quik/Media/Color/LockIO.cs
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Quik.Media.Color
|
||||||
|
{
|
||||||
|
public unsafe struct LockIO
|
||||||
|
{
|
||||||
|
public QImageLock Lock { get; }
|
||||||
|
public int Width => Lock.Width;
|
||||||
|
public int Height => Lock.Height;
|
||||||
|
public int Depth => Depth;
|
||||||
|
public QImageFormat Format => Lock.Format;
|
||||||
|
|
||||||
|
public LockIO(QImageLock imageLock)
|
||||||
|
{
|
||||||
|
if (!imageLock.Format.IsU8())
|
||||||
|
throw new Exception("Can only read/write U8 format images");
|
||||||
|
|
||||||
|
Lock = imageLock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QColor this[int index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int chan = Format.Channels();
|
||||||
|
byte *ptr = (byte*)Lock.ImagePtr + chan * index;
|
||||||
|
|
||||||
|
switch (Format)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case QImageFormat.RedU8: return new QColor(ptr[0], 0, 0, 255);
|
||||||
|
case QImageFormat.AlphaU8: return new QColor(0, 0, 0, ptr[0]);
|
||||||
|
case QImageFormat.RaU8: return new QColor(ptr[0], 0, 0, ptr[1]);
|
||||||
|
case QImageFormat.RgbU8: return new QColor(ptr[0], ptr[1], ptr[2], 255);
|
||||||
|
case QImageFormat.RgbaU8: return new QColor(ptr[0], ptr[1], ptr[2], ptr[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
int chan = Format.Channels();
|
||||||
|
byte *ptr = (byte*)Lock.ImagePtr + chan * index;
|
||||||
|
|
||||||
|
switch (Format)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case QImageFormat.RedU8:
|
||||||
|
ptr[0] = value.R;
|
||||||
|
break;
|
||||||
|
case QImageFormat.AlphaU8:
|
||||||
|
ptr[0] = value.A;
|
||||||
|
break;
|
||||||
|
case QImageFormat.RaU8:
|
||||||
|
ptr[0] = value.R;
|
||||||
|
ptr[1] = value.A;
|
||||||
|
break;
|
||||||
|
case QImageFormat.RgbU8:
|
||||||
|
ptr[0] = value.R;
|
||||||
|
ptr[1] = value.G;
|
||||||
|
ptr[2] = value.B;
|
||||||
|
break;
|
||||||
|
case QImageFormat.RgbaU8:
|
||||||
|
ptr[0] = value.R;
|
||||||
|
ptr[1] = value.G;
|
||||||
|
ptr[2] = value.B;
|
||||||
|
ptr[3] = value.A;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public QColor this[int x, int y, int z = 0]
|
||||||
|
{
|
||||||
|
get => this[x + y * Width + z * Width * Height];
|
||||||
|
set => this[x + y * Width + z * Width * Height] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe struct LockIOF
|
||||||
|
{
|
||||||
|
public QImageLock Lock { get; }
|
||||||
|
public int Width => Lock.Width;
|
||||||
|
public int Height => Lock.Height;
|
||||||
|
public int Depth => Depth;
|
||||||
|
public QImageFormat Format => Lock.Format;
|
||||||
|
|
||||||
|
public LockIOF(QImageLock imageLock)
|
||||||
|
{
|
||||||
|
if (!imageLock.Format.IsFloat())
|
||||||
|
throw new Exception("Can only read/write U8 format images");
|
||||||
|
|
||||||
|
Lock = imageLock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QColorF this[int index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int chan = Format.Channels();
|
||||||
|
float *ptr = (float*)Lock.ImagePtr + chan * index;
|
||||||
|
|
||||||
|
switch (Format)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case QImageFormat.RedU8: return new QColorF(ptr[0], 0, 0, 255);
|
||||||
|
case QImageFormat.AlphaU8: return new QColorF(0, 0, 0, ptr[0]);
|
||||||
|
case QImageFormat.RaU8: return new QColorF(ptr[0], 0, 0, ptr[1]);
|
||||||
|
case QImageFormat.RgbU8: return new QColorF(ptr[0], ptr[1], ptr[2], 255);
|
||||||
|
case QImageFormat.RgbaU8: return new QColorF(ptr[0], ptr[1], ptr[2], ptr[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
int chan = Format.Channels();
|
||||||
|
float *ptr = (float*)Lock.ImagePtr + chan * index;
|
||||||
|
|
||||||
|
switch (Format)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case QImageFormat.RedU8:
|
||||||
|
ptr[0] = value.R;
|
||||||
|
break;
|
||||||
|
case QImageFormat.AlphaU8:
|
||||||
|
ptr[0] = value.A;
|
||||||
|
break;
|
||||||
|
case QImageFormat.RaU8:
|
||||||
|
ptr[0] = value.R;
|
||||||
|
ptr[1] = value.A;
|
||||||
|
break;
|
||||||
|
case QImageFormat.RgbU8:
|
||||||
|
ptr[0] = value.R;
|
||||||
|
ptr[1] = value.G;
|
||||||
|
ptr[2] = value.B;
|
||||||
|
break;
|
||||||
|
case QImageFormat.RgbaU8:
|
||||||
|
ptr[0] = value.R;
|
||||||
|
ptr[1] = value.G;
|
||||||
|
ptr[2] = value.B;
|
||||||
|
ptr[3] = value.A;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public QColorF this[int x, int y, int z = 0]
|
||||||
|
{
|
||||||
|
get => this[x + y * Width + z * Width * Height];
|
||||||
|
set => this[x + y * Width + z * Width * Height] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
71
Quik/Media/Extensions.cs
Normal file
71
Quik/Media/Extensions.cs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
namespace Quik.Media
|
||||||
|
{
|
||||||
|
public static class Extensions
|
||||||
|
{
|
||||||
|
public static bool IsU8(this QImageFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case QImageFormat.AlphaU8:
|
||||||
|
case QImageFormat.RedU8:
|
||||||
|
case QImageFormat.RaU8:
|
||||||
|
case QImageFormat.RgbU8:
|
||||||
|
case QImageFormat.RgbaU8:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsFloat(this QImageFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case QImageFormat.AlphaF:
|
||||||
|
case QImageFormat.RedF:
|
||||||
|
case QImageFormat.RaF:
|
||||||
|
case QImageFormat.RgbF:
|
||||||
|
case QImageFormat.RgbaF:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int BitsPerPixel(this QImageFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case QImageFormat.AlphaU8: return sizeof(byte);
|
||||||
|
case QImageFormat.RedU8: return sizeof(byte);
|
||||||
|
case QImageFormat.RaU8: return 2 * sizeof(byte);
|
||||||
|
case QImageFormat.RgbU8: return 3 * sizeof(byte);
|
||||||
|
case QImageFormat.RgbaU8: return 4 * sizeof(byte);
|
||||||
|
case QImageFormat.AlphaF: return sizeof(float);
|
||||||
|
case QImageFormat.RedF: return sizeof(float);
|
||||||
|
case QImageFormat.RaF: return 2 * sizeof(float);
|
||||||
|
case QImageFormat.RgbF: return 3 * sizeof(float);
|
||||||
|
case QImageFormat.RgbaF: return 4 * sizeof(float);
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Channels(this QImageFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case QImageFormat.AlphaU8: return 1;
|
||||||
|
case QImageFormat.RedU8: return 1;
|
||||||
|
case QImageFormat.RaU8: return 2;
|
||||||
|
case QImageFormat.RgbU8: return 3;
|
||||||
|
case QImageFormat.RgbaU8: return 4;
|
||||||
|
case QImageFormat.AlphaF: return 1;
|
||||||
|
case QImageFormat.RedF: return 1;
|
||||||
|
case QImageFormat.RaF: return 2;
|
||||||
|
case QImageFormat.RgbF: return 3;
|
||||||
|
case QImageFormat.RgbaF: return 4;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,12 +7,13 @@ namespace Quik.Media
|
|||||||
public abstract int Width { get; }
|
public abstract int Width { get; }
|
||||||
public abstract int Height { get; }
|
public abstract int Height { get; }
|
||||||
public abstract int Depth { get; }
|
public abstract int Depth { get; }
|
||||||
|
public abstract QImageFormat InternalFormat { get; }
|
||||||
public virtual int MipMapLevels => 0;
|
public virtual int MipMapLevels => 0;
|
||||||
public virtual bool Premultiplied => false;
|
public virtual bool Premultiplied => false;
|
||||||
|
|
||||||
public abstract IntPtr LockBits2d(QImageFormat format, int level = 0);
|
public abstract void LockBits2d(out QImageLock imageLock, QImageLockOptions options);
|
||||||
public abstract IntPtr LockBits3d(QImageFormat format, int level = 0);
|
public abstract void LockBits3d(out QImageLock imageLock, QImageLockOptions options);
|
||||||
public abstract IntPtr LockBits3d(QImageFormat format, int depth, int level = 0);
|
public abstract void LockBits3d(out QImageLock imageLock, QImageLockOptions options, int depth);
|
||||||
public abstract void UnlockBits();
|
public abstract void UnlockBits();
|
||||||
|
|
||||||
// IDisposable
|
// IDisposable
|
||||||
@ -28,4 +29,37 @@ namespace Quik.Media
|
|||||||
protected virtual void Dispose(bool disposing) { }
|
protected virtual void Dispose(bool disposing) { }
|
||||||
public void Dispose() => DisposePrivate(true);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user