@ -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, |
Rgb565, |
||||
Rgba8888, |
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 |