diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml
index 4319e8343e..3858eaa6ff 100644
--- a/api/Avalonia.nupkg.xml
+++ b/api/Avalonia.nupkg.xml
@@ -19,6 +19,12 @@
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0001
+ T:Avalonia.Platform.IReadableBitmapWithAlphaImpl
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
CP0001
T:Avalonia.Utilities.StringTokenizer
@@ -55,6 +61,12 @@
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0001
+ T:Avalonia.Platform.IReadableBitmapWithAlphaImpl
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
CP0001
T:Avalonia.Utilities.StringTokenizer
@@ -127,6 +139,12 @@
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Media.Imaging.Bitmap.CopyPixels(Avalonia.Platform.ILockedFramebuffer,Avalonia.Platform.AlphaFormat)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
CP0002
M:Avalonia.Media.StreamGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)
@@ -229,6 +247,12 @@
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Platform.LockedFramebuffer.#ctor(System.IntPtr,Avalonia.PixelSize,System.Int32,Avalonia.Vector,Avalonia.Platform.PixelFormat,System.Action)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
CP0002
M:Avalonia.Visuals.Platform.PathGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)
@@ -481,6 +505,12 @@
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Media.Imaging.Bitmap.CopyPixels(Avalonia.Platform.ILockedFramebuffer,Avalonia.Platform.AlphaFormat)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
CP0002
M:Avalonia.Media.StreamGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)
@@ -583,6 +613,12 @@
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Platform.LockedFramebuffer.#ctor(System.IntPtr,Avalonia.PixelSize,System.Int32,Avalonia.Vector,Avalonia.Platform.PixelFormat,System.Action)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
CP0002
M:Avalonia.Visuals.Platform.PathGeometryContext.ArcTo(Avalonia.Point,Avalonia.Size,System.Double,System.Boolean,Avalonia.Media.SweepDirection)
@@ -853,6 +889,18 @@
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0006
+ P:Avalonia.Platform.ILockedFramebuffer.AlphaFormat
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0006
+ P:Avalonia.Platform.IReadableBitmapImpl.AlphaFormat
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
CP0006
M:Avalonia.Input.Platform.IClipboard.SetDataAsync(Avalonia.Input.IAsyncDataTransfer)
@@ -1003,6 +1051,18 @@
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0006
+ P:Avalonia.Platform.ILockedFramebuffer.AlphaFormat
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0006
+ P:Avalonia.Platform.IReadableBitmapImpl.AlphaFormat
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
CP0006
M:Avalonia.OpenGL.IGlExternalSemaphore.SignalTimelineSemaphore(Avalonia.OpenGL.IGlExternalImageTexture,System.UInt64)
@@ -1087,12 +1147,24 @@
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0008
+ T:Avalonia.Platform.IWriteableBitmapImpl
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
CP0008
T:Avalonia.Media.StreamGeometryContext
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0008
+ T:Avalonia.Platform.IWriteableBitmapImpl
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
CP0009
T:Avalonia.Platform.Screen
diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs
index 7a0d9793a3..87710127aa 100644
--- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs
+++ b/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);
diff --git a/src/Avalonia.Base/Media/Imaging/Bitmap.cs b/src/Avalonia.Base/Media/Imaging/Bitmap.cs
index 2a1ce15feb..9c0c20d170 100644
--- a/src/Avalonia.Base/Media/Imaging/Bitmap.cs
+++ b/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.
///
/// The target buffer.
- /// The alpha format.
///
- 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
diff --git a/src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs b/src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs
index 435e51009d..f2b3b4d0c7 100644
--- a/src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs
+++ b/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);
});
}
diff --git a/src/Avalonia.Base/Platform/ILockedFramebuffer.cs b/src/Avalonia.Base/Platform/ILockedFramebuffer.cs
index f963b77cd9..a73b339fcc 100644
--- a/src/Avalonia.Base/Platform/ILockedFramebuffer.cs
+++ b/src/Avalonia.Base/Platform/ILockedFramebuffer.cs
@@ -29,6 +29,9 @@ namespace Avalonia.Platform
///
PixelFormat Format { get; }
- //TODO12: Add AlphaFormat
+ ///
+ /// Gets the alpha format.
+ ///
+ AlphaFormat AlphaFormat { get; }
}
}
diff --git a/src/Avalonia.Base/Platform/IReadableBitmapImpl.cs b/src/Avalonia.Base/Platform/IReadableBitmapImpl.cs
index d5a0c765cc..6332d302af 100644
--- a/src/Avalonia.Base/Platform/IReadableBitmapImpl.cs
+++ b/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();
}
diff --git a/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs b/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs
index 685491a326..185b116c9a 100644
--- a/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs
+++ b/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs
@@ -5,8 +5,8 @@ namespace Avalonia.Platform
///
/// Defines the platform-specific interface for a .
///
- [Unstable]
- public interface IWriteableBitmapImpl : IBitmapImpl, IReadableBitmapWithAlphaImpl
+ [PrivateApi]
+ public interface IWriteableBitmapImpl : IBitmapImpl, IReadableBitmapImpl
{
}
}
diff --git a/src/Avalonia.Base/Platform/LockedFramebuffer.cs b/src/Avalonia.Base/Platform/LockedFramebuffer.cs
index b9094d9e14..8924947662 100644
--- a/src/Avalonia.Base/Platform/LockedFramebuffer.cs
+++ b/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()
{
diff --git a/src/Avalonia.Base/Platform/RetainedFramebuffer.cs b/src/Avalonia.Base/Platform/RetainedFramebuffer.cs
index e5ca1070dc..e1983afde1 100644
--- a/src/Avalonia.Base/Platform/RetainedFramebuffer.cs
+++ b/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;
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs
index 593adfb225..cc7d7608ff 100644
--- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.Framebuffer.cs
+++ b/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();
diff --git a/src/Avalonia.Native/DeferredFramebuffer.cs b/src/Avalonia.Native/DeferredFramebuffer.cs
index fd3f3ce676..a352fc2e8b 100644
--- a/src/Avalonia.Native/DeferredFramebuffer.cs
+++ b/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()
{
diff --git a/src/Avalonia.X11/X11CursorFactory.cs b/src/Avalonia.X11/X11CursorFactory.cs
index 2af06960bc..c6be968bfd 100644
--- a/src/Avalonia.X11/X11CursorFactory.cs
+++ b/src/Avalonia.X11/X11CursorFactory.cs
@@ -133,7 +133,7 @@ namespace Avalonia.X11
return new LockedFramebuffer(
_blob.Address + Marshal.SizeOf(),
_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);
diff --git a/src/Avalonia.X11/X11FramebufferSurface.cs b/src/Avalonia.X11/X11FramebufferSurface.cs
index 5b01ce91de..4efd7f072d 100644
--- a/src/Avalonia.X11/X11FramebufferSurface.cs
+++ b/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);
diff --git a/src/Avalonia.X11/X11IconLoader.cs b/src/Avalonia.X11/X11IconLoader.cs
index bde6de6614..1cfe009062 100644
--- a/src/Avalonia.X11/X11IconLoader.cs
+++ b/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());
}
diff --git a/src/Browser/Avalonia.Browser/Rendering/BrowserSoftwareRenderTarget.cs b/src/Browser/Avalonia.Browser/Rendering/BrowserSoftwareRenderTarget.cs
index b3395e87d9..6a0fb61f70 100644
--- a/src/Browser/Avalonia.Browser/Rendering/BrowserSoftwareRenderTarget.cs
+++ b/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);
}
-}
\ No newline at end of file
+}
diff --git a/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
index 0a29b9a5b8..05d835c57c 100644
--- a/src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
+++ b/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));
}
}
diff --git a/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs b/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
index e4138bee66..8f80e22138 100644
--- a/src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
+++ b/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()
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs
index 97b730c160..f9df24ed69 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs
+++ b/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()
diff --git a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
index 318cdac22c..46be609ec0 100644
--- a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
+++ b/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;
diff --git a/src/Skia/Avalonia.Skia/ImmutableBitmap.cs b/src/Skia/Avalonia.Skia/ImmutableBitmap.cs
index 0372f25047..44408a5333 100644
--- a/src/Skia/Avalonia.Skia/ImmutableBitmap.cs
+++ b/src/Skia/Avalonia.Skia/ImmutableBitmap.cs
@@ -10,7 +10,7 @@ namespace Avalonia.Skia
///
/// Immutable Skia bitmap.
///
- 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);
}
}
}
diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
index 7d8b247ad4..f57f84b168 100644
--- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
+++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
@@ -238,6 +238,8 @@ namespace Avalonia.Skia
public Vector Dpi => _parent.Dpi;
///
public PixelFormat Format => _bitmap.ColorType.ToPixelFormat();
+
+ public AlphaFormat AlphaFormat => _bitmap.AlphaType.ToAlphaFormat();
}
}
}
diff --git a/src/Windows/Avalonia.Win32/FramebufferManager.cs b/src/Windows/Avalonia.Win32/FramebufferManager.cs
index 5c8e84aaa6..7c11277c57 100644
--- a/src/Windows/Avalonia.Win32/FramebufferManager.cs
+++ b/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
{
diff --git a/src/Windows/Avalonia.Win32/Interop/Win32Icon.cs b/src/Windows/Avalonia.Win32/Interop/Win32Icon.cs
index 57da43da92..1fe301526b 100644
--- a/src/Windows/Avalonia.Win32/Interop/Win32Icon.cs
+++ b/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));
}
diff --git a/tests/Avalonia.RenderTests/Composition/DirectFbCompositionTests.cs b/tests/Avalonia.RenderTests/Composition/DirectFbCompositionTests.cs
index 1a8d7d9272..3a48bafaad 100644
--- a/tests/Avalonia.RenderTests/Composition/DirectFbCompositionTests.cs
+++ b/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
diff --git a/tests/Avalonia.RenderTests/Media/BitmapTests.cs b/tests/Avalonia.RenderTests/Media/BitmapTests.cs
index 0f849a0400..54e336a6f4 100644
--- a/tests/Avalonia.RenderTests/Media/BitmapTests.cs
+++ b/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();
using(var cpuContext = r.CreateBackendContext(null))
using (var target = cpuContext.CreateRenderTarget(new object[] { fb }))
diff --git a/tests/Avalonia.UnitTests/CompositorTestServices.cs b/tests/Avalonia.UnitTests/CompositorTestServices.cs
index ec481328b7..96b3248ed1 100644
--- a/tests/Avalonia.UnitTests/CompositorTestServices.cs
+++ b/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);