@ -0,0 +1,51 @@ |
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
using System.Threading; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.Media.Imaging; |
|||
|
|||
internal class BitmapMemory : IDisposable |
|||
{ |
|||
private readonly int _memorySize; |
|||
|
|||
public BitmapMemory(PixelFormat format, PixelSize size) |
|||
{ |
|||
Format = format; |
|||
Size = size; |
|||
RowBytes = (size.Width * format.BitsPerPixel + 7) / 8; |
|||
_memorySize = RowBytes * size.Height; |
|||
Address = Marshal.AllocHGlobal(_memorySize); |
|||
GC.AddMemoryPressure(_memorySize); |
|||
} |
|||
|
|||
private void ReleaseUnmanagedResources() |
|||
{ |
|||
if (Address != IntPtr.Zero) |
|||
{ |
|||
GC.RemoveMemoryPressure(_memorySize); |
|||
Marshal.FreeHGlobal(Address); |
|||
} |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
ReleaseUnmanagedResources(); |
|||
GC.SuppressFinalize(this); |
|||
} |
|||
|
|||
~BitmapMemory() |
|||
{ |
|||
ReleaseUnmanagedResources(); |
|||
} |
|||
|
|||
public IntPtr Address { get; private set; } |
|||
public PixelSize Size { get; } |
|||
public int RowBytes { get; } |
|||
public PixelFormat Format { get; } |
|||
|
|||
|
|||
|
|||
public void CopyToRgba(IntPtr buffer, int rowBytes) => |
|||
PixelFormatReader.Transcode(buffer, Address, Size, RowBytes, rowBytes, Format); |
|||
} |
|||
@ -0,0 +1,280 @@ |
|||
using System; |
|||
using Avalonia.Platform; |
|||
namespace Avalonia.Media.Imaging; |
|||
|
|||
internal struct Rgba8888Pixel |
|||
{ |
|||
public byte R; |
|||
public byte G; |
|||
public byte B; |
|||
public byte A; |
|||
} |
|||
|
|||
static unsafe class PixelFormatReader |
|||
{ |
|||
public interface IPixelFormatReader |
|||
{ |
|||
Rgba8888Pixel ReadNext(); |
|||
void Reset(IntPtr address); |
|||
} |
|||
|
|||
private static readonly Rgba8888Pixel s_white = new Rgba8888Pixel |
|||
{ |
|||
A = 255, |
|||
B = 255, |
|||
G = 255, |
|||
R = 255 |
|||
}; |
|||
|
|||
private static readonly Rgba8888Pixel s_black = new Rgba8888Pixel |
|||
{ |
|||
A = 255, |
|||
B = 0, |
|||
G = 0, |
|||
R = 0 |
|||
}; |
|||
|
|||
public unsafe struct BlackWhitePixelReader : IPixelFormatReader |
|||
{ |
|||
private int _bit; |
|||
private byte* _address; |
|||
|
|||
public void Reset(IntPtr address) |
|||
{ |
|||
_address = (byte*)address; |
|||
_bit = 0; |
|||
} |
|||
|
|||
public Rgba8888Pixel ReadNext() |
|||
{ |
|||
var shift = 7 - _bit; |
|||
var value = (*_address >> shift) & 1; |
|||
_bit++; |
|||
if (_bit == 8) |
|||
{ |
|||
_address++; |
|||
_bit = 0; |
|||
} |
|||
return value == 1 ? s_white : s_black; |
|||
} |
|||
} |
|||
|
|||
public unsafe struct Gray2PixelReader : IPixelFormatReader |
|||
{ |
|||
private int _bit; |
|||
private byte* _address; |
|||
|
|||
public void Reset(IntPtr address) |
|||
{ |
|||
_address = (byte*)address; |
|||
_bit = 0; |
|||
} |
|||
|
|||
private static Rgba8888Pixel[] Palette = new[] |
|||
{ |
|||
s_black, |
|||
new Rgba8888Pixel |
|||
{ |
|||
A = 255, B = 0x55, G = 0x55, R = 0x55 |
|||
}, |
|||
new Rgba8888Pixel |
|||
{ |
|||
A = 255, B = 0xAA, G = 0xAA, R = 0xAA |
|||
}, |
|||
s_white |
|||
}; |
|||
|
|||
public Rgba8888Pixel ReadNext() |
|||
{ |
|||
var shift = 6 - _bit; |
|||
var value = (byte)((*_address >> shift)); |
|||
value = (byte)((value & 3)); |
|||
_bit += 2; |
|||
if (_bit == 8) |
|||
{ |
|||
_address++; |
|||
_bit = 0; |
|||
} |
|||
|
|||
return Palette[value]; |
|||
} |
|||
} |
|||
|
|||
public unsafe struct Gray4PixelReader : IPixelFormatReader |
|||
{ |
|||
private int _bit; |
|||
private byte* _address; |
|||
|
|||
public void Reset(IntPtr address) |
|||
{ |
|||
_address = (byte*)address; |
|||
_bit = 0; |
|||
} |
|||
|
|||
public Rgba8888Pixel ReadNext() |
|||
{ |
|||
var shift = 4 - _bit; |
|||
var value = (byte)((*_address >> shift)); |
|||
value = (byte)((value & 0xF)); |
|||
value = (byte)(value | (value << 4)); |
|||
_bit += 4; |
|||
if (_bit == 8) |
|||
{ |
|||
_address++; |
|||
_bit = 0; |
|||
} |
|||
|
|||
return new Rgba8888Pixel |
|||
{ |
|||
A = 255, |
|||
B = value, |
|||
G = value, |
|||
R = value |
|||
}; |
|||
} |
|||
} |
|||
|
|||
public unsafe struct Gray8PixelReader : IPixelFormatReader |
|||
{ |
|||
private byte* _address; |
|||
public void Reset(IntPtr address) |
|||
{ |
|||
_address = (byte*)address; |
|||
} |
|||
|
|||
public Rgba8888Pixel ReadNext() |
|||
{ |
|||
var value = *_address; |
|||
_address++; |
|||
|
|||
return new Rgba8888Pixel |
|||
{ |
|||
A = 255, |
|||
B = value, |
|||
G = value, |
|||
R = value |
|||
}; |
|||
} |
|||
} |
|||
|
|||
public unsafe struct Gray16PixelReader : IPixelFormatReader |
|||
{ |
|||
private ushort* _address; |
|||
public Rgba8888Pixel ReadNext() |
|||
{ |
|||
var value16 = *_address; |
|||
_address++; |
|||
var value8 = (byte)(value16 >> 8); |
|||
return new Rgba8888Pixel |
|||
{ |
|||
A = 255, |
|||
B = value8, |
|||
G = value8, |
|||
R = value8 |
|||
}; |
|||
} |
|||
|
|||
public void Reset(IntPtr address) => _address = (ushort*)address; |
|||
} |
|||
|
|||
public unsafe struct Gray32FloatPixelReader : IPixelFormatReader |
|||
{ |
|||
private byte* _address; |
|||
public Rgba8888Pixel ReadNext() |
|||
{ |
|||
var f = *(float*)_address; |
|||
var srgb = Math.Pow(f, 1 / 2.2); |
|||
var value = (byte)(srgb * 255); |
|||
|
|||
_address += 4; |
|||
return new Rgba8888Pixel |
|||
{ |
|||
A = 255, |
|||
B = value, |
|||
G = value, |
|||
R = value |
|||
}; |
|||
} |
|||
|
|||
public void Reset(IntPtr address) => _address = (byte*)address; |
|||
} |
|||
|
|||
struct Rgba64 |
|||
{ |
|||
#pragma warning disable CS0649
|
|||
public ushort R; |
|||
public ushort G; |
|||
public ushort B; |
|||
public ushort A; |
|||
#pragma warning restore CS0649
|
|||
} |
|||
|
|||
public unsafe struct Rgba64PixelFormatReader : IPixelFormatReader |
|||
{ |
|||
private Rgba64* _address; |
|||
public Rgba8888Pixel ReadNext() |
|||
{ |
|||
var value = *_address; |
|||
|
|||
_address++; |
|||
return new Rgba8888Pixel |
|||
{ |
|||
A = (byte)(value.A >> 8), |
|||
B = (byte)(value.B >> 8), |
|||
G = (byte)(value.G >> 8), |
|||
R = (byte)(value.R >> 8), |
|||
}; |
|||
} |
|||
|
|||
public void Reset(IntPtr address) => _address = (Rgba64*)address; |
|||
} |
|||
|
|||
public static void Transcode(IntPtr dst, IntPtr src, PixelSize size, int strideSrc, int strideDst, |
|||
PixelFormat format) |
|||
{ |
|||
if (format == PixelFormats.BlackWhite) |
|||
Transcode<BlackWhitePixelReader>(dst, src, size, strideSrc, strideDst); |
|||
else if (format == PixelFormats.Gray2) |
|||
Transcode<Gray2PixelReader>(dst, src, size, strideSrc, strideDst); |
|||
else if (format == PixelFormats.Gray4) |
|||
Transcode<Gray4PixelReader>(dst, src, size, strideSrc, strideDst); |
|||
else if (format == PixelFormats.Gray8) |
|||
Transcode<Gray8PixelReader>(dst, src, size, strideSrc, strideDst); |
|||
else if (format == PixelFormats.Gray16) |
|||
Transcode<Gray16PixelReader>(dst, src, size, strideSrc, strideDst); |
|||
else if (format == PixelFormats.Gray32Float) |
|||
Transcode<Gray32FloatPixelReader>(dst, src, size, strideSrc, strideDst); |
|||
else if (format == PixelFormats.Rgba64) |
|||
Transcode<Rgba64PixelFormatReader>(dst, src, size, strideSrc, strideDst); |
|||
else |
|||
throw new NotSupportedException($"Pixel format {format} is not supported"); |
|||
} |
|||
|
|||
public static bool SupportsFormat(PixelFormat format) |
|||
{ |
|||
return format == PixelFormats.BlackWhite |
|||
|| format == PixelFormats.Gray2 |
|||
|| format == PixelFormats.Gray4 |
|||
|| format == PixelFormats.Gray8 |
|||
|| format == PixelFormats.Gray16 |
|||
|| format == PixelFormats.Gray32Float |
|||
|| format == PixelFormats.Rgba64; |
|||
} |
|||
|
|||
public static void Transcode<TReader>(IntPtr dst, IntPtr src, PixelSize size, int strideSrc, int strideDst) where TReader : struct, IPixelFormatReader |
|||
{ |
|||
var w = size.Width; |
|||
var h = size.Height; |
|||
TReader reader = default; |
|||
for (var y = 0; y < h; y++) |
|||
{ |
|||
reader.Reset(src + strideSrc * y); |
|||
var dstRow = (Rgba8888Pixel*)(dst + strideDst * y); |
|||
for (var x = 0; x < w; x++) |
|||
{ |
|||
*dstRow = reader.ReadNext(); |
|||
dstRow++; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Avalonia.Platform; |
|||
|
|||
public interface IReadableBitmapImpl |
|||
{ |
|||
PixelFormat? Format { get; } |
|||
ILockedFramebuffer Lock(); |
|||
} |
|||
@ -1,9 +1,74 @@ |
|||
namespace Avalonia.Platform |
|||
using System; |
|||
|
|||
namespace Avalonia.Platform |
|||
{ |
|||
public enum PixelFormat |
|||
internal enum PixelFormatEnum |
|||
{ |
|||
Rgb565, |
|||
Rgba8888, |
|||
Bgra8888 |
|||
Bgra8888, |
|||
BlackWhite, |
|||
Gray2, |
|||
Gray4, |
|||
Gray8, |
|||
Gray16, |
|||
Gray32Float, |
|||
Rgba64 |
|||
} |
|||
|
|||
public record struct PixelFormat |
|||
{ |
|||
internal PixelFormatEnum FormatEnum; |
|||
|
|||
public int BitsPerPixel |
|||
{ |
|||
get |
|||
{ |
|||
if (FormatEnum == PixelFormatEnum.BlackWhite) |
|||
return 1; |
|||
else if (FormatEnum == PixelFormatEnum.Gray2) |
|||
return 2; |
|||
else if (FormatEnum == PixelFormatEnum.Gray4) |
|||
return 4; |
|||
else if (FormatEnum == PixelFormatEnum.Gray8) |
|||
return 8; |
|||
else if (FormatEnum == PixelFormatEnum.Rgb565 |
|||
|| FormatEnum == PixelFormatEnum.Gray16) |
|||
return 16; |
|||
else if (FormatEnum == PixelFormatEnum.Rgba64) |
|||
return 64; |
|||
|
|||
return 32; |
|||
} |
|||
} |
|||
|
|||
internal bool HasAlpha => FormatEnum == PixelFormatEnum.Rgba8888 |
|||
|| FormatEnum == PixelFormatEnum.Bgra8888 |
|||
|| FormatEnum == PixelFormatEnum.Rgba64; |
|||
|
|||
internal PixelFormat(PixelFormatEnum format) |
|||
{ |
|||
FormatEnum = format; |
|||
} |
|||
|
|||
public static PixelFormat Rgb565 => PixelFormats.Rgb565; |
|||
public static PixelFormat Rgba8888 => PixelFormats.Rgba8888; |
|||
public static PixelFormat Bgra8888 => PixelFormats.Bgra8888; |
|||
|
|||
public override string ToString() => FormatEnum.ToString(); |
|||
} |
|||
|
|||
public static class PixelFormats |
|||
{ |
|||
public static PixelFormat Rgb565 { get; } = new PixelFormat(PixelFormatEnum.Rgb565); |
|||
public static PixelFormat Rgba8888 { get; } = new PixelFormat(PixelFormatEnum.Rgba8888); |
|||
public static PixelFormat Rgba64 { get; } = new PixelFormat(PixelFormatEnum.Rgba64); |
|||
public static PixelFormat Bgra8888 { get; } = new PixelFormat(PixelFormatEnum.Bgra8888); |
|||
public static PixelFormat BlackWhite { get; } = new PixelFormat(PixelFormatEnum.BlackWhite); |
|||
public static PixelFormat Gray2 { get; } = new PixelFormat(PixelFormatEnum.Gray2); |
|||
public static PixelFormat Gray4 { get; } = new PixelFormat(PixelFormatEnum.Gray4); |
|||
public static PixelFormat Gray8 { get; } = new PixelFormat(PixelFormatEnum.Gray8); |
|||
public static PixelFormat Gray16 { get; } = new PixelFormat(PixelFormatEnum.Gray16); |
|||
public static PixelFormat Gray32Float { get; } = new PixelFormat(PixelFormatEnum.Gray32Float); |
|||
} |
|||
} |
|||
|
|||
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 25 KiB |