splitter/Splitter-UI/Services/MatToBitmapConverter.cs

137 lines
3.7 KiB
C#

using Avalonia;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
namespace Splitter_UI.Services;
public sealed class MatToBitmapConverter(IBufferPool _pool) : IMatToBitmapConverter
{
private readonly object _sync = new();
public Bitmap Convert(Mat mat, Bitmap? existing = null)
{
if (mat.Empty())
throw new ArgumentException("Mat is empty.", nameof(mat));
var w = mat.Width;
var h = mat.Height;
var channels = mat.Channels();
if (channels != 3 && channels != 4)
throw new NotSupportedException($"Only 3 or 4 channel Mats are supported. Got {channels}.");
lock (_sync)
{
var entry = _pool.Get(w, h);
var src = mat;
if (!src.IsContinuous())
src = src.Clone();
unsafe
{
var srcPtr = (byte*)src.DataPointer;
var totalBytes = w * h * channels;
if (channels == 3)
{
fixed (byte* dstBgr = entry.Bgr)
{
Buffer.MemoryCopy(srcPtr, dstBgr, entry.Bgr.Length, totalBytes);
}
ConvertBgrToBgra(entry.Bgr, entry.Bgra, w, h);
}
else
{
fixed (byte* dstBgra = entry.Bgra)
{
Buffer.MemoryCopy(srcPtr, dstBgra, entry.Bgra.Length, totalBytes);
}
}
}
if (existing is WriteableBitmap wb &&
wb.PixelSize.Width == w &&
wb.PixelSize.Height == h)
{
UpdateWriteableBitmap(wb, entry.Bgra, w, h);
return wb;
}
return CreateBitmap(entry.Bgra, w, h);
}
}
public Bitmap Convert(byte[] bgr, int width, int height, Bitmap? existing = null)
{
var entry = _pool.Get(width, height);
ConvertBgrToBgra(bgr, entry.Bgra, width, height);
if (existing is WriteableBitmap wb &&
wb.PixelSize.Width == width &&
wb.PixelSize.Height == height)
{
UpdateWriteableBitmap(wb, entry.Bgra, width, height);
return wb;
}
return CreateBitmap(entry.Bgra, width, height);
}
private static void ConvertBgrToBgra(byte[] bgr, byte[] bgra, int width, int height)
{
var si = 0;
var di = 0;
var totalPixels = width * height;
for (var i = 0; i < totalPixels; i++)
{
bgra[di + 0] = bgr[si + 0];
bgra[di + 1] = bgr[si + 1];
bgra[di + 2] = bgr[si + 2];
bgra[di + 3] = 255;
si += 3;
di += 4;
}
}
private static unsafe void UpdateWriteableBitmap(WriteableBitmap wb, byte[] bgra, int width, int height)
{
using var fb = wb.Lock();
var dstPtr = (byte*)fb.Address;
var dstStride = fb.RowBytes;
var srcStride = width * 4;
fixed (byte* srcPtr = bgra)
{
for (var y = 0; y < height; y++)
{
var srcRow = srcPtr + y * srcStride;
var dstRow = dstPtr + y * dstStride;
Buffer.MemoryCopy(srcRow, dstRow, dstStride, srcStride);
}
}
}
private static unsafe Bitmap CreateBitmap(byte[] bgra, int width, int height)
{
var stride = width * 4;
fixed (byte* p = bgra)
{
return new WriteableBitmap(
PixelFormat.Bgra8888,
AlphaFormat.Premul,
(nint)p,
new PixelSize(width, height),
new Vector(96, 96),
stride);
}
}
}