Browse Source

Add AlphaFormat to ILockedFramebuffer (#20556)

* Add AlphaFormat to ILockedFramebuffer

* Update API suppressions
pull/18827/merge
Julien Lebosquain 1 week ago
committed by GitHub
parent
commit
4c3c9aa99f
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 72
      api/Avalonia.nupkg.xml
  2. 7
      src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs
  3. 14
      src/Avalonia.Base/Media/Imaging/Bitmap.cs
  4. 4
      src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs
  5. 5
      src/Avalonia.Base/Platform/ILockedFramebuffer.cs
  6. 11
      src/Avalonia.Base/Platform/IReadableBitmapImpl.cs
  7. 4
      src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs
  8. 4
      src/Avalonia.Base/Platform/LockedFramebuffer.cs
  9. 14
      src/Avalonia.Base/Platform/RetainedFramebuffer.cs
  10. 1
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs
  11. 1
      src/Avalonia.Native/DeferredFramebuffer.cs
  12. 2
      src/Avalonia.X11/X11CursorFactory.cs
  13. 2
      src/Avalonia.X11/X11FramebufferSurface.cs
  14. 2
      src/Avalonia.X11/X11IconLoader.cs
  15. 4
      src/Browser/Avalonia.Browser/Rendering/BrowserSoftwareRenderTarget.cs
  16. 6
      src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
  17. 1
      src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
  18. 11
      src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs
  19. 2
      src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
  20. 6
      src/Skia/Avalonia.Skia/ImmutableBitmap.cs
  21. 2
      src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
  22. 2
      src/Windows/Avalonia.Win32/FramebufferManager.cs
  23. 14
      src/Windows/Avalonia.Win32/Interop/Win32Icon.cs
  24. 2
      tests/Avalonia.RenderTests/Composition/DirectFbCompositionTests.cs
  25. 7
      tests/Avalonia.RenderTests/Media/BitmapTests.cs
  26. 2
      tests/Avalonia.UnitTests/CompositorTestServices.cs

72
api/Avalonia.nupkg.xml

@ -19,6 +19,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Platform.IReadableBitmapWithAlphaImpl</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Utilities.StringTokenizer</Target>
@ -55,6 +61,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Platform.IReadableBitmapWithAlphaImpl</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Utilities.StringTokenizer</Target>
@ -127,6 +139,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Media.Imaging.Bitmap.CopyPixels(Avalonia.Platform.ILockedFramebuffer,Avalonia.Platform.AlphaFormat)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Media.StreamGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)</Target>
@ -229,6 +247,12 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.LockedFramebuffer.#ctor(System.IntPtr,Avalonia.PixelSize,System.Int32,Avalonia.Vector,Avalonia.Platform.PixelFormat,System.Action)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Visuals.Platform.PathGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)</Target>
@ -481,6 +505,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Media.Imaging.Bitmap.CopyPixels(Avalonia.Platform.ILockedFramebuffer,Avalonia.Platform.AlphaFormat)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Media.StreamGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)</Target>
@ -583,6 +613,12 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Platform.LockedFramebuffer.#ctor(System.IntPtr,Avalonia.PixelSize,System.Int32,Avalonia.Vector,Avalonia.Platform.PixelFormat,System.Action)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Visuals.Platform.PathGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)</Target>
@ -853,6 +889,18 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Avalonia.Platform.ILockedFramebuffer.AlphaFormat</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Avalonia.Platform.IReadableBitmapImpl.AlphaFormat</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer)</Target>
@ -1003,6 +1051,18 @@
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Avalonia.Platform.ILockedFramebuffer.AlphaFormat</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Avalonia.Platform.IReadableBitmapImpl.AlphaFormat</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64)</Target>
@ -1087,12 +1147,24 @@
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0008</DiagnosticId>
<Target>T:Avalonia.Platform.IWriteableBitmapImpl</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0008</DiagnosticId>
<Target>T:Avalonia.Media.StreamGeometryContext</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0008</DiagnosticId>
<Target>T:Avalonia.Platform.IWriteableBitmapImpl</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Base.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0009</DiagnosticId>
<Target>T:Avalonia.Platform.Screen</Target>

7
src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs

@ -25,10 +25,10 @@ namespace Avalonia.Android.Platform.SkiaPlatform
Size = new PixelSize(rc.right, rc.bottom);
ANativeWindow_lock(_window, &buffer, &rc);
Format = buffer.format == AndroidPixelFormat.WINDOW_FORMAT_RGB_565
? PixelFormat.Rgb565 : PixelFormat.Rgba8888;
(Format, AlphaFormat, RowBytes) = buffer.format == AndroidPixelFormat.WINDOW_FORMAT_RGB_565 ?
(PixelFormat.Rgb565, AlphaFormat.Opaque, buffer.stride * 2) :
(PixelFormat.Rgba8888, AlphaFormat.Premul, buffer.stride * 4);
RowBytes = buffer.stride * (Format == PixelFormat.Rgb565 ? 2 : 4);
Address = buffer.bits;
Dpi = new Vector(96, 96) * scaling;
@ -46,6 +46,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public int RowBytes { get; }
public Vector Dpi { get; }
public PixelFormat Format { get; }
public AlphaFormat AlphaFormat { get; }
[DllImport("android")]
internal static extern IntPtr ANativeWindow_fromSurface(IntPtr jniEnv, IntPtr handle);

14
src/Avalonia.Base/Media/Imaging/Bitmap.cs

@ -1,5 +1,4 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;
using Avalonia.Platform;
@ -177,7 +176,7 @@ namespace Avalonia.Media.Imaging
public virtual PixelFormat? Format => (PlatformImpl.Item as IReadableBitmapImpl)?.Format;
public virtual AlphaFormat? AlphaFormat => (PlatformImpl.Item as IReadableBitmapWithAlphaImpl)?.AlphaFormat;
public virtual AlphaFormat? AlphaFormat => (PlatformImpl.Item as IReadableBitmapImpl)?.AlphaFormat;
private protected unsafe void CopyPixelsCore(PixelRect sourceRect, IntPtr buffer, int bufferSize, int stride,
ILockedFramebuffer fb)
@ -237,16 +236,15 @@ namespace Avalonia.Media.Imaging
/// 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)
public void CopyPixels(ILockedFramebuffer buffer)
{
if (PlatformImpl.Item is not IReadableBitmapWithAlphaImpl readable || readable.Format == null || readable.AlphaFormat == null)
if (PlatformImpl.Item is not IReadableBitmapImpl readable || readable.Format == null || readable.AlphaFormat == null)
{
throw new NotSupportedException("CopyPixels is not supported for this bitmap type");
}
if (buffer.Format != readable.Format || alphaFormat != readable.AlphaFormat)
if (buffer.Format != readable.Format || buffer.AlphaFormat != readable.AlphaFormat)
{
using (var fb = readable.Lock())
{
@ -255,11 +253,11 @@ namespace Avalonia.Media.Imaging
fb.Size,
fb.RowBytes,
fb.Format,
readable.AlphaFormat.Value,
fb.AlphaFormat,
buffer.Address,
buffer.RowBytes,
buffer.Format,
alphaFormat);
buffer.AlphaFormat);
}
}
else

4
src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs

@ -71,10 +71,10 @@ namespace Avalonia.Media.Imaging
return new LockedFramebuffer(_pixelFormatMemory.Address, _pixelFormatMemory.Size,
_pixelFormatMemory.RowBytes,
Dpi, _pixelFormatMemory.Format, () =>
Dpi, _pixelFormatMemory.Format, _pixelFormatMemory.AlphaFormat, () =>
{
using var inner = ((IWriteableBitmapImpl)PlatformImpl.Item).Lock();
_pixelFormatMemory.CopyToRgba(Platform.AlphaFormat.Unpremul, inner.Address, inner.RowBytes);
_pixelFormatMemory.CopyToRgba(inner.AlphaFormat, inner.Address, inner.RowBytes);
});
}

5
src/Avalonia.Base/Platform/ILockedFramebuffer.cs

@ -29,6 +29,9 @@ namespace Avalonia.Platform
/// </summary>
PixelFormat Format { get; }
//TODO12: Add AlphaFormat
/// <summary>
/// Gets the alpha format.
/// </summary>
AlphaFormat AlphaFormat { get; }
}
}

11
src/Avalonia.Base/Platform/IReadableBitmapImpl.cs

@ -2,15 +2,10 @@ using Avalonia.Metadata;
namespace Avalonia.Platform;
public interface IReadableBitmapImpl
[PrivateApi]
public interface IReadableBitmapImpl : IBitmapImpl
{
PixelFormat? Format { get; }
ILockedFramebuffer Lock();
}
//TODO12: Remove me once we can change IReadableBitmapImpl
[Unstable]
public interface IReadableBitmapWithAlphaImpl : IReadableBitmapImpl
{
AlphaFormat? AlphaFormat { get; }
ILockedFramebuffer Lock();
}

4
src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs

@ -5,8 +5,8 @@ namespace Avalonia.Platform
/// <summary>
/// Defines the platform-specific interface for a <see cref="Avalonia.Media.Imaging.WriteableBitmap"/>.
/// </summary>
[Unstable]
public interface IWriteableBitmapImpl : IBitmapImpl, IReadableBitmapWithAlphaImpl
[PrivateApi]
public interface IWriteableBitmapImpl : IBitmapImpl, IReadableBitmapImpl
{
}
}

4
src/Avalonia.Base/Platform/LockedFramebuffer.cs

@ -7,7 +7,7 @@ namespace Avalonia.Platform
private readonly Action? _onDispose;
public LockedFramebuffer(IntPtr address, PixelSize size, int rowBytes, Vector dpi, PixelFormat format,
Action? onDispose)
AlphaFormat alphaFormat, Action? onDispose)
{
_onDispose = onDispose;
Address = address;
@ -15,6 +15,7 @@ namespace Avalonia.Platform
RowBytes = rowBytes;
Dpi = dpi;
Format = format;
AlphaFormat = alphaFormat;
}
public IntPtr Address { get; }
@ -22,6 +23,7 @@ namespace Avalonia.Platform
public int RowBytes { get; }
public Vector Dpi { get; }
public PixelFormat Format { get; }
public AlphaFormat AlphaFormat { get; }
public void Dispose()
{

14
src/Avalonia.Base/Platform/RetainedFramebuffer.cs

@ -1,6 +1,4 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.Metadata;
using Avalonia.Platform.Internal;
namespace Avalonia.Platform;
@ -10,6 +8,7 @@ internal class RetainedFramebuffer : IDisposable
public PixelSize Size { get; }
public int RowBytes { get; }
public PixelFormat Format { get; }
public AlphaFormat AlphaFormat { get; }
public IntPtr Address => _blob?.Address ?? throw new ObjectDisposedException(nameof(RetainedFramebuffer));
private UnmanagedBlob? _blob;
@ -17,13 +16,13 @@ internal class RetainedFramebuffer : IDisposable
? format
: throw new ArgumentOutOfRangeException(nameof(format));
public RetainedFramebuffer(PixelSize size, PixelFormat format) : this(size, ValidateKnownFormat(format),
format.BitsPerPixel / 8 * size.Width)
public RetainedFramebuffer(PixelSize size, PixelFormat format, AlphaFormat alphaFormat)
: this(size, ValidateKnownFormat(format), alphaFormat, format.BitsPerPixel / 8 * size.Width)
{
}
public RetainedFramebuffer(PixelSize size, PixelFormat format, int rowBytes)
public RetainedFramebuffer(PixelSize size, PixelFormat format, AlphaFormat alphaFormat, int rowBytes)
{
if (size.Width <= 0 || size.Height <= 0)
throw new ArgumentOutOfRangeException(nameof(size));
@ -32,6 +31,7 @@ internal class RetainedFramebuffer : IDisposable
Size = size;
RowBytes = rowBytes;
Format = format;
AlphaFormat = alphaFormat;
_blob = new UnmanagedBlob(RowBytes * size.Height);
}
@ -39,7 +39,7 @@ internal class RetainedFramebuffer : IDisposable
{
if (_blob == null)
throw new ObjectDisposedException(nameof(RetainedFramebuffer));
return new LockedFramebuffer(_blob.Address, Size, RowBytes, dpi, Format, () =>
return new LockedFramebuffer(_blob.Address, Size, RowBytes, dpi, Format, AlphaFormat, () =>
{
blit(this);
GC.KeepAlive(this);
@ -51,4 +51,4 @@ internal class RetainedFramebuffer : IDisposable
_blob?.Dispose();
_blob = null;
}
}
}

1
src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs

@ -76,6 +76,7 @@ namespace Avalonia.Controls.Remote.Server
Stride,
new Vector(_dpi, _dpi),
new PlatformPixelFormat((PixelFormatEnum)Format),
Format == ProtocolPixelFormat.Rgb565 ? AlphaFormat.Opaque : AlphaFormat.Premul,
() =>
{
handle.Free();

1
src/Avalonia.Native/DeferredFramebuffer.cs

@ -29,6 +29,7 @@ namespace Avalonia.Native
public int RowBytes { get; set; }
public Vector Dpi { get; set; }
public PixelFormat Format { get; set; }
public AlphaFormat AlphaFormat { get; set; }
public void Dispose()
{

2
src/Avalonia.X11/X11CursorFactory.cs

@ -133,7 +133,7 @@ namespace Avalonia.X11
return new LockedFramebuffer(
_blob.Address + Marshal.SizeOf<XcursorImage>(),
_pixelSize, _pixelSize.Width * 4,
new Vector(96, 96), PixelFormat.Bgra8888, null);
new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Premul, null);
}
public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(Lock);

2
src/Avalonia.X11/X11FramebufferSurface.cs

@ -61,7 +61,7 @@ namespace Avalonia.X11
{
_fb?.Dispose();
_fb = null;
_fb = new RetainedFramebuffer(new PixelSize(width, height), PixelFormat.Bgra8888);
_fb = new RetainedFramebuffer(new PixelSize(width, height), PixelFormat.Bgra8888, AlphaFormat.Premul);
}
properties = new FramebufferLockProperties(framebufferValid);

2
src/Avalonia.X11/X11IconLoader.cs

@ -83,7 +83,7 @@ namespace Avalonia.X11
{
var h = GCHandle.Alloc(_bdata, GCHandleType.Pinned);
return new LockedFramebuffer(h.AddrOfPinnedObject(), new PixelSize(_width, _height), _width * 4,
new Vector(96, 96), PixelFormat.Bgra8888,
new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Premul,
() => h.Free());
}

4
src/Browser/Avalonia.Browser/Rendering/BrowserSoftwareRenderTarget.cs

@ -47,7 +47,7 @@ partial class BrowserSoftwareRenderTarget : BrowserRenderTarget, IFramebufferPla
{
_fb?.Dispose();
_fb = null;
_fb = new RetainedFramebuffer(size, PixelFormat.Rgba8888);
_fb = new RetainedFramebuffer(size, PixelFormat.Rgba8888, AlphaFormat.Premul);
}
return _fb.Lock(new Vector(scaling * 96, scaling * 96), _parent._blit);
@ -66,4 +66,4 @@ partial class BrowserSoftwareRenderTarget : BrowserRenderTarget, IFramebufferPla
{
PutPixelData(Js, fb.Address.ToInt32(), fb.Size.Width * fb.Size.Height * 4, fb.Size.Width, fb.Size.Height);
}
}
}

6
src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs

@ -414,8 +414,8 @@ namespace Avalonia.Headless
public Vector Dpi { get; }
public PixelSize PixelSize { get; }
public PixelFormat? Format { get; }
public AlphaFormat? AlphaFormat { get; }
public PixelFormat? Format => PixelFormat.Rgba8888;
public AlphaFormat? AlphaFormat => Platform.AlphaFormat.Premul;
public int Version { get; set; }
public void Save(string fileName, int? quality = null)
@ -434,7 +434,7 @@ namespace Avalonia.Headless
Version++;
var mem = Marshal.AllocHGlobal(PixelSize.Width * PixelSize.Height * 4);
return new LockedFramebuffer(mem, PixelSize, PixelSize.Width * 4, Dpi, PixelFormat.Rgba8888,
() => Marshal.FreeHGlobal(mem));
Platform.AlphaFormat.Premul, () => Marshal.FreeHGlobal(mem));
}
}

1
src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs

@ -200,6 +200,7 @@ namespace Avalonia.Headless
public int RowBytes => _fb.RowBytes;
public Vector Dpi => _fb.Dpi;
public PixelFormat Format => _fb.Format;
public AlphaFormat AlphaFormat => _fb.AlphaFormat;
}
public ILockedFramebuffer Lock()

11
src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs

@ -101,12 +101,17 @@ namespace Avalonia.LinuxFramebuffer.Output
public static LockedFramebuffer LockFb(IntPtr address, fb_var_screeninfo varInfo,
fb_fix_screeninfo fixedInfo, Vector dpi, Action? dispose)
{
var (format, alphaFormat) = varInfo switch
{
{ bits_per_pixel: 16 } => (PixelFormat.Rgb565, AlphaFormat.Opaque),
{ bits_per_pixel: 32, blue.offset: 16 } => (PixelFormat.Rgba8888, AlphaFormat.Premul),
_ => (PixelFormat.Bgra8888, AlphaFormat.Premul)
};
return new LockedFramebuffer(address,
new PixelSize((int)varInfo.xres, (int)varInfo.yres),
(int)fixedInfo.line_length, dpi,
varInfo.bits_per_pixel == 16 ? PixelFormat.Rgb565
: varInfo.blue.offset == 16 ? PixelFormat.Rgba8888
: PixelFormat.Bgra8888, dispose);
format, alphaFormat, dispose);
}
private void BlitToDevice()

2
src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs

@ -67,7 +67,7 @@ namespace Avalonia.Skia
var framebuffer = _renderTargetWithProperties?.Lock(out lockProperties) ?? _renderTarget.Lock();
var framebufferImageInfo = new SKImageInfo(framebuffer.Size.Width, framebuffer.Size.Height,
framebuffer.Format.ToSkColorType(),
framebuffer.Format == PixelFormat.Rgb565 ? SKAlphaType.Opaque : SKAlphaType.Premul);
framebuffer.AlphaFormat.ToSkAlphaType());
CreateSurface(framebufferImageInfo, framebuffer);
_hadConversionShim |= _conversionShim != null;

6
src/Skia/Avalonia.Skia/ImmutableBitmap.cs

@ -10,7 +10,7 @@ namespace Avalonia.Skia
/// <summary>
/// Immutable Skia bitmap.
/// </summary>
internal class ImmutableBitmap : IDrawableBitmapImpl, IReadableBitmapWithAlphaImpl
internal class ImmutableBitmap : IDrawableBitmapImpl, IReadableBitmapImpl
{
private readonly SKImage _image;
private readonly SKBitmap? _bitmap;
@ -195,7 +195,9 @@ namespace Avalonia.Skia
if (_bitmap.ColorType.ToAvalonia() is not { } format)
throw new NotSupportedException($"Unsupported format {_bitmap.ColorType}");
return new LockedFramebuffer(_bitmap.GetPixels(), PixelSize, _bitmap.RowBytes, Dpi, format, null);
var alphaFormat = _bitmap.AlphaType.ToAlphaFormat();
return new LockedFramebuffer(_bitmap.GetPixels(), PixelSize, _bitmap.RowBytes, Dpi, format, alphaFormat, null);
}
}
}

2
src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs

@ -238,6 +238,8 @@ namespace Avalonia.Skia
public Vector Dpi => _parent.Dpi;
/// <inheritdoc />
public PixelFormat Format => _bitmap.ColorType.ToPixelFormat();
public AlphaFormat AlphaFormat => _bitmap.AlphaType.ToAlphaFormat();
}
}
}

2
src/Windows/Avalonia.Win32/FramebufferManager.cs

@ -49,7 +49,7 @@ namespace Avalonia.Win32
return fb = new LockedFramebuffer(
framebufferData.Data.Address, framebufferData.Size, framebufferData.RowBytes,
GetCurrentDpi(), s_format, _onDisposeAction);
GetCurrentDpi(), s_format, AlphaFormat.Premul, _onDisposeAction);
}
finally
{

14
src/Windows/Avalonia.Win32/Interop/Win32Icon.cs

@ -96,14 +96,14 @@ internal class Win32Icon : IDisposable
static IntPtr CreateHBitmap(Bitmap source)
{
using var fb = AllocFramebuffer(source.PixelSize, PixelFormats.Bgra8888);
source.CopyPixels(fb, AlphaFormat.Unpremul);
using var fb = AllocFramebuffer(source.PixelSize, PixelFormats.Bgra8888, AlphaFormat.Unpremul);
source.CopyPixels(fb);
return UnmanagedMethods.CreateBitmap(source.PixelSize.Width, source.PixelSize.Height, 1, 32, fb.Address);
}
static unsafe IntPtr AlphaToMask(Bitmap source)
{
using var alphaMaskBuffer = AllocFramebuffer(source.PixelSize, PixelFormats.BlackWhite);
using var alphaMaskBuffer = AllocFramebuffer(source.PixelSize, PixelFormats.BlackWhite, AlphaFormat.Opaque);
var height = alphaMaskBuffer.Size.Height;
var width = alphaMaskBuffer.Size.Width;
@ -114,8 +114,8 @@ internal class Win32Icon : IDisposable
}
else
{
using var argbBuffer = AllocFramebuffer(source.PixelSize, PixelFormat.Bgra8888);
source.CopyPixels(argbBuffer, AlphaFormat.Unpremul);
using var argbBuffer = AllocFramebuffer(source.PixelSize, PixelFormat.Bgra8888, AlphaFormat.Unpremul);
source.CopyPixels(argbBuffer);
var pSource = (byte*)argbBuffer.Address;
var pDest = (byte*)alphaMaskBuffer.Address;
@ -140,7 +140,7 @@ internal class Win32Icon : IDisposable
return UnmanagedMethods.CreateBitmap(width, height, 1, 1, alphaMaskBuffer.Address);
}
static LockedFramebuffer AllocFramebuffer(PixelSize size, PixelFormat format)
static LockedFramebuffer AllocFramebuffer(PixelSize size, PixelFormat format, AlphaFormat alphaFormat)
{
if (size.Width < 1 || size.Height < 1)
throw new ArgumentOutOfRangeException();
@ -149,7 +149,7 @@ internal class Win32Icon : IDisposable
var data = Marshal.AllocHGlobal(size.Height * stride);
if (data == IntPtr.Zero)
throw new OutOfMemoryException();
return new LockedFramebuffer(data, size, stride, new Vector(96, 96), format,
return new LockedFramebuffer(data, size, stride, new Vector(96, 96), format, alphaFormat,
() => Marshal.FreeHGlobal(data));
}

2
tests/Avalonia.RenderTests/Composition/DirectFbCompositionTests.cs

@ -83,7 +83,7 @@ public class DirectFbCompositionTests : TestBase
SKBitmap fb = new SKBitmap(200, 200, SKColorType.Rgba8888, SKAlphaType.Premul);
ILockedFramebuffer LockFb() => new LockedFramebuffer(fb.GetAddress(0, 0), new(fb.Width, fb.Height),
fb.RowBytes, new Vector(96, 96), PixelFormat.Rgba8888, null);
fb.RowBytes, new Vector(96, 96), PixelFormat.Rgba8888, AlphaFormat.Premul, null);
bool previousFrameIsRetained = false;
IFramebufferRenderTarget rt = advertised

7
tests/Avalonia.RenderTests/Media/BitmapTests.cs

@ -25,10 +25,11 @@ namespace Avalonia.Skia.RenderTests
class Framebuffer : ILockedFramebuffer, IFramebufferPlatformSurface
{
public Framebuffer(PixelFormat fmt, PixelSize size)
public Framebuffer(PixelFormat fmt, AlphaFormat alphaFormat, PixelSize size)
{
Format = fmt;
var bpp = fmt == PixelFormat.Rgb565 ? 2 : 4;
AlphaFormat = alphaFormat;
Size = size;
RowBytes = bpp * size.Width;
Address = Marshal.AllocHGlobal(size.Height * RowBytes);
@ -40,6 +41,8 @@ namespace Avalonia.Skia.RenderTests
public PixelFormat Format { get; }
public AlphaFormat AlphaFormat { get; }
public PixelSize Size { get; }
public int RowBytes { get; }
@ -64,7 +67,7 @@ namespace Avalonia.Skia.RenderTests
{
var fmt = new PixelFormat(fmte);
var testName = nameof(FramebufferRenderResultsShouldBeUsableAsBitmap) + "_" + fmt;
var fb = new Framebuffer(fmt, new PixelSize(80, 80));
var fb = new Framebuffer(fmt, AlphaFormat.Premul, new PixelSize(80, 80));
var r = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>();
using(var cpuContext = r.CreateBackendContext(null))
using (var target = cpuContext.CreateRenderTarget(new object[] { fb }))

2
tests/Avalonia.UnitTests/CompositorTestServices.cs

@ -174,7 +174,7 @@ public class CompositorTestServices : IDisposable
{
var ptr = Marshal.AllocHGlobal(128);
return new LockedFramebuffer(ptr, new PixelSize(1, 1), 4, new Vector(96, 96),
PixelFormat.Rgba8888, () => Marshal.FreeHGlobal(ptr));
PixelFormat.Rgba8888, AlphaFormat.Premul, () => Marshal.FreeHGlobal(ptr));
}
public IFramebufferRenderTarget CreateFramebufferRenderTarget() => new FuncFramebufferRenderTarget(Lock);

Loading…
Cancel
Save