Skip to content

Commit

Permalink
Introduce PixelFormatWriter (#12807)
Browse files Browse the repository at this point in the history
Introduce PixelFormatTranscoder
Introduce Bitmap.CopyPixels that transcodes pixel and alpha format
  • Loading branch information
Gillibald authored Sep 14, 2023
1 parent 23dd9fb commit fac9e55
Show file tree
Hide file tree
Showing 17 changed files with 1,093 additions and 112 deletions.
68 changes: 56 additions & 12 deletions src/Avalonia.Base/Media/Imaging/Bitmap.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Avalonia.Platform;
using Avalonia.Utilities;

Expand Down Expand Up @@ -108,19 +108,23 @@ public Bitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSiz
PlatformImpl = RefCountable.Create(factory.LoadBitmap(format, alphaFormat, data, size, dpi, stride));
else
{
var transcoded = Marshal.AllocHGlobal(size.Width * size.Height * 4);
var transcodedStride = size.Width * 4;
try
using (var transcoded = new BitmapMemory(PixelFormat.Rgba8888, Platform.AlphaFormat.Unpremul, size))
{
PixelFormatReader.Transcode(transcoded, data, size, stride, transcodedStride, format);
var transcodedAlphaFormat = format.HasAlpha ? alphaFormat : AlphaFormat.Opaque;

var transcodedAlphaFormat = format.HasAlpha ? alphaFormat : Platform.AlphaFormat.Opaque;

PixelFormatTranscoder.Transcode(
data,
size,
stride,
format,
alphaFormat,
transcoded.Address,
transcoded.RowBytes,
transcoded.Format,
transcodedAlphaFormat);

PlatformImpl = RefCountable.Create(factory.LoadBitmap(PixelFormat.Rgba8888, transcodedAlphaFormat,
transcoded, size, dpi, transcodedStride));
}
finally
{
Marshal.FreeHGlobal(transcoded);
transcoded.Address, size, dpi, transcoded.RowBytes));
}

_isTranscoded = true;
Expand Down Expand Up @@ -173,6 +177,8 @@ public void Save(Stream stream, int? quality = null)

public virtual PixelFormat? Format => (PlatformImpl.Item as IReadableBitmapImpl)?.Format;

public virtual AlphaFormat? AlphaFormat => (PlatformImpl.Item as IReadableBitmapWithAlphaImpl)?.AlphaFormat;

private protected unsafe void CopyPixelsCore(PixelRect sourceRect, IntPtr buffer, int bufferSize, int stride,
ILockedFramebuffer fb)
{
Expand Down Expand Up @@ -222,6 +228,44 @@ public virtual void CopyPixels(PixelRect sourceRect, IntPtr buffer, int bufferSi
CopyPixelsCore(sourceRect, buffer, bufferSize, stride, fb);
}

/// <summary>
/// Copies pixels to the target buffer and transcodes the pixel and alpha format if needed.
/// </summary>
/// <param name="buffer">The target buffer.</param>
/// <param name="alphaFormat">The alpha format.</param>
/// <exception cref="NotSupportedException"></exception>
public void CopyPixels(ILockedFramebuffer buffer, AlphaFormat alphaFormat)
{
if (PlatformImpl.Item is not IReadableBitmapWithAlphaImpl readable || readable.Format == null || readable.AlphaFormat == null)
{
throw new NotSupportedException("CopyPixels is not supported for this bitmap type");
}

if (readable.Format != Format || readable.AlphaFormat != alphaFormat)
{
using (var fb = readable.Lock())
{
PixelFormatTranscoder.Transcode(
fb.Address,
fb.Size,
fb.RowBytes,
fb.Format,
readable.AlphaFormat.Value,
buffer.Address,
buffer.RowBytes,
buffer.Format,
alphaFormat);
}
}
else
{
using (var fb = readable.Lock())
{
CopyPixelsCore(new PixelRect(fb.Size), buffer.Address, buffer.RowBytes * buffer.Size.Height, fb.RowBytes, fb);
}
}
}

/// <inheritdoc/>
void IImage.Draw(
DrawingContext context,
Expand Down
22 changes: 17 additions & 5 deletions src/Avalonia.Base/Media/Imaging/BitmapMemory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ internal class BitmapMemory : IDisposable
{
private readonly int _memorySize;

public BitmapMemory(PixelFormat format, PixelSize size)
public BitmapMemory(PixelFormat format, AlphaFormat alphaFormat, PixelSize size)
{
Format = format;
AlphaFormat = alphaFormat;
Size = size;
RowBytes = (size.Width * format.BitsPerPixel + 7) / 8;
_memorySize = RowBytes * size.Height;
Expand Down Expand Up @@ -44,8 +45,19 @@ public void Dispose()
public int RowBytes { get; }
public PixelFormat Format { get; }

public AlphaFormat AlphaFormat { get; }


public void CopyToRgba(IntPtr buffer, int rowBytes) =>
PixelFormatReader.Transcode(buffer, Address, Size, RowBytes, rowBytes, Format);
}
public void CopyToRgba(AlphaFormat alphaFormat, IntPtr buffer, int stride)
{
PixelFormatTranscoder.Transcode(
Address,
Size,
RowBytes,
Format,
AlphaFormat,
buffer,
stride,
PixelFormat.Rgba8888,
alphaFormat);
}
}
Loading

0 comments on commit fac9e55

Please sign in to comment.