Browse Source

Merge remote-tracking branch 'origin/master' into fixes/dc-render-layers

Conflicts:
	src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
	src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs
pull/1184/head
Steven Kirk 9 years ago
parent
commit
e0a6771296
  1. 15
      src/Avalonia.Controls/MenuItem.cs
  2. 9
      src/Avalonia.Controls/StackPanel.cs
  3. 4
      src/Avalonia.Controls/TreeView.cs
  4. 15
      src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs
  5. 26
      src/Avalonia.Visuals/Rendering/SceneGraph/DrawOperation.cs
  6. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
  7. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs
  8. 11
      src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs
  9. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs
  10. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/OpacityMaskNode.cs
  11. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs
  12. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs
  13. 32
      src/Gtk/Avalonia.Gtk3/FramebufferManager.cs
  14. 5
      src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
  15. 2
      src/Gtk/Avalonia.Gtk3/IDeferredRenderOperation.cs
  16. 11
      src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
  17. 4
      src/Gtk/Avalonia.Gtk3/Interop/Native.cs
  18. 5
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  19. 54
      src/Gtk/Avalonia.Gtk3/X11.cs
  20. 55
      src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs
  21. 1
      src/Shared/PlatformSupport/StandardRuntimePlatform.cs
  22. 22
      src/Skia/Avalonia.Skia/BitmapImpl.cs
  23. 7
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  24. 2
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  25. 84
      src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs
  26. 18
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs
  27. 2
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
  28. 2
      tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs
  29. 4
      tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs
  30. 2
      tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs
  31. 8
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
  32. 2
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
  33. 4
      tests/Avalonia.Controls.UnitTests/StackPanelTests.cs
  34. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_RelativeSource.cs
  35. 4
      tests/Avalonia.Markup.Xaml.UnitTests/Templates/MemberSelectorTests.cs
  36. 11
      tests/Avalonia.RenderTests/Media/BitmapTests.cs
  37. 55
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs
  38. BIN
      tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Bgra8888.expected.png
  39. BIN
      tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Rgb565.expected.png
  40. BIN
      tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Rgba8888.expected.png

15
src/Avalonia.Controls/MenuItem.cs

@ -307,6 +307,21 @@ namespace Avalonia.Controls
() => IsSubMenuOpen = true,
TimeSpan.FromMilliseconds(400));
}
else
{
var parentItem = Parent as MenuItem;
if (parentItem != null)
{
foreach (var sibling in parentItem.Items
.OfType<MenuItem>()
.Where(x => x != this && x.IsSubMenuOpen))
{
sibling.CloseSubmenus();
sibling.IsSubMenuOpen = false;
sibling.IsSelected = false;
}
}
}
}
/// <summary>

9
src/Avalonia.Controls/StackPanel.cs

@ -170,6 +170,15 @@ namespace Avalonia.Controls
}
}
if (Orientation == Orientation.Vertical)
{
measuredHeight -= gap;
}
else
{
measuredWidth -= gap;
}
return new Size(measuredWidth, measuredHeight);
}

4
src/Avalonia.Controls/TreeView.cs

@ -253,9 +253,7 @@ namespace Avalonia.Controls
if (AutoScrollToSelectedItem)
{
DispatcherTimer.RunOnce(
container.ContainerControl.BringIntoView,
TimeSpan.Zero);
Dispatcher.UIThread.InvokeAsync(container.ContainerControl.BringIntoView);
}
break;

15
src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
@ -12,20 +11,16 @@ namespace Avalonia.Rendering.SceneGraph
/// <summary>
/// Base class for draw operations that can use a brush.
/// </summary>
internal abstract class BrushDrawOperation : IDrawOperation
internal abstract class BrushDrawOperation : DrawOperation
{
/// <inheritdoc/>
public abstract Rect Bounds { get; }
/// <inheritdoc/>
public abstract bool HitTest(Point p);
public BrushDrawOperation(Rect bounds, Matrix transform, Pen pen)
: base(bounds, transform, pen)
{
}
/// <summary>
/// Gets a collection of child scenes that are needed to draw visual brushes.
/// </summary>
public abstract IDictionary<IVisual, Scene> ChildScenes { get; }
/// <inheritdoc/>
public abstract void Render(IDrawingContextImpl context);
}
}

26
src/Avalonia.Visuals/Rendering/SceneGraph/DrawOperation.cs

@ -0,0 +1,26 @@
using System;
using Avalonia.Media;
using Avalonia.Platform;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// Base class for draw operations that have bounds.
/// </summary>
internal abstract class DrawOperation : IDrawOperation
{
public DrawOperation(Rect bounds, Matrix transform, Pen pen)
{
bounds = bounds.Inflate((pen?.Thickness ?? 0) / 2).TransformToAABB(transform);
Bounds = new Rect(
new Point(Math.Floor(bounds.X), Math.Floor(bounds.Y)),
new Point(Math.Ceiling(bounds.Right), Math.Ceiling(bounds.Bottom)));
}
public Rect Bounds { get; }
public abstract bool HitTest(Point p);
public abstract void Render(IDrawingContextImpl context);
}
}

5
src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs

@ -28,8 +28,8 @@ namespace Avalonia.Rendering.SceneGraph
Pen pen,
IGeometryImpl geometry,
IDictionary<IVisual, Scene> childScenes = null)
: base(geometry.GetRenderBounds(pen?.Thickness ?? 0), transform, null)
{
Bounds = geometry.GetRenderBounds(pen?.Thickness ?? 0).TransformToAABB(transform);
Transform = transform;
Brush = brush?.ToImmutable();
Pen = pen?.ToImmutable();
@ -37,9 +37,6 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes;
}
/// <inheritdoc/>
public override Rect Bounds { get; }
/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>

2
src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs

@ -12,7 +12,7 @@ namespace Avalonia.Rendering.SceneGraph
public interface IDrawOperation
{
/// <summary>
/// Gets the bounds of the visible content in the node.
/// Gets the bounds of the visible content in the node in global coordinates.
/// </summary>
Rect Bounds { get; }

11
src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs

@ -9,7 +9,7 @@ namespace Avalonia.Rendering.SceneGraph
/// <summary>
/// A node in the scene graph which represents an image draw.
/// </summary>
internal class ImageNode : IDrawOperation
internal class ImageNode : DrawOperation
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageNode"/> class.
@ -20,8 +20,8 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="sourceRect">The source rect.</param>
/// <param name="destRect">The destination rect.</param>
public ImageNode(Matrix transform, IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
: base(destRect, transform, null)
{
Bounds = destRect.TransformToAABB(transform);
Transform = transform;
Source = source;
Opacity = opacity;
@ -29,9 +29,6 @@ namespace Avalonia.Rendering.SceneGraph
DestRect = destRect;
}
/// <inheritdoc/>
public Rect Bounds { get; }
/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>
@ -80,7 +77,7 @@ namespace Avalonia.Rendering.SceneGraph
}
/// <inheritdoc/>
public void Render(IDrawingContextImpl context)
public override void Render(IDrawingContextImpl context)
{
// TODO: Probably need to introduce some kind of locking mechanism in the case of
// WriteableBitmap.
@ -89,6 +86,6 @@ namespace Avalonia.Rendering.SceneGraph
}
/// <inheritdoc/>
public bool HitTest(Point p) => Bounds.Contains(p);
public override bool HitTest(Point p) => Bounds.Contains(p);
}
}

5
src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs

@ -28,8 +28,8 @@ namespace Avalonia.Rendering.SceneGraph
Point p1,
Point p2,
IDictionary<IVisual, Scene> childScenes = null)
: base(new Rect(p1, p2), transform, pen)
{
Bounds = new Rect(p1, p2).TransformToAABB(transform).Inflate(pen?.Thickness ?? 0);
Transform = transform;
Pen = pen?.ToImmutable();
P1 = p1;
@ -37,9 +37,6 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes;
}
/// <inheritdoc/>
public override Rect Bounds { get; }
/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>

5
src/Avalonia.Visuals/Rendering/SceneGraph/OpacityMaskNode.cs

@ -19,6 +19,7 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="bounds">The bounds of the mask.</param>
/// <param name="childScenes">Child scenes for drawing visual brushes.</param>
public OpacityMaskNode(IBrush mask, Rect bounds, IDictionary<IVisual, Scene> childScenes = null)
: base(Rect.Empty, Matrix.Identity, null)
{
Mask = mask?.ToImmutable();
MaskBounds = bounds;
@ -30,12 +31,10 @@ namespace Avalonia.Rendering.SceneGraph
/// opacity mask pop.
/// </summary>
public OpacityMaskNode()
: base(Rect.Empty, Matrix.Identity, null)
{
}
/// <inheritdoc/>
public override Rect Bounds => Rect.Empty;
/// <summary>
/// Gets the mask to be pushed or null if the operation represents a pop.
/// </summary>

5
src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs

@ -30,8 +30,8 @@ namespace Avalonia.Rendering.SceneGraph
Rect rect,
float cornerRadius,
IDictionary<IVisual, Scene> childScenes = null)
: base(rect, transform, pen)
{
Bounds = rect.TransformToAABB(transform).Inflate(pen?.Thickness ?? 0);
Transform = transform;
Brush = brush?.ToImmutable();
Pen = pen?.ToImmutable();
@ -40,9 +40,6 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes;
}
/// <inheritdoc/>
public override Rect Bounds { get; }
/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>

5
src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs

@ -28,8 +28,8 @@ namespace Avalonia.Rendering.SceneGraph
Point origin,
IFormattedTextImpl text,
IDictionary<IVisual, Scene> childScenes = null)
: base(new Rect(origin, text.Size), transform, null)
{
Bounds = new Rect(origin, text.Size).TransformToAABB(transform);
Transform = transform;
Foreground = foreground?.ToImmutable();
Origin = origin;
@ -37,9 +37,6 @@ namespace Avalonia.Rendering.SceneGraph
ChildScenes = childScenes;
}
/// <inheritdoc/>
public override Rect Bounds { get; }
/// <summary>
/// Gets the transform with which the node will be drawn.
/// </summary>

32
src/Gtk/Avalonia.Gtk3/FramebufferManager.cs

@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Platform;
using Avalonia.Threading;
namespace Avalonia.Gtk3
{
@ -27,7 +28,38 @@ namespace Avalonia.Gtk3
var s = _window.ClientSize;
var width = (int) s.Width;
var height = (int) s.Height;
if (!Dispatcher.UIThread.CheckAccess() && Gtk3Platform.DisplayClassName.ToLower().Contains("x11"))
{
var x11 = LockX11Framebuffer(width, height);
if (x11 != null)
return x11;
}
return new ImageSurfaceFramebuffer(_window, width, height, _window.LastKnownScaleFactor);
}
private static int X11ErrorHandler(IntPtr d, IntPtr e)
{
return 0;
}
private static X11.XErrorHandler X11ErrorHandlerDelegate = X11ErrorHandler;
private static IntPtr X11Display;
private ILockedFramebuffer LockX11Framebuffer(int width, int height)
{
if (!_window.GdkWindowHandle.HasValue)
return null;
if (X11Display == IntPtr.Zero)
{
X11Display = X11.XOpenDisplay(IntPtr.Zero);
if (X11Display == IntPtr.Zero)
return null;
X11.XSetErrorHandler(X11ErrorHandlerDelegate);
}
return new X11Framebuffer(X11Display, _window.GdkWindowHandle.Value, width, height, _window.LastKnownScaleFactor);
}
}
}

5
src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -22,11 +23,15 @@ namespace Avalonia.Gtk3
internal static readonly MouseDevice Mouse = new MouseDevice();
internal static readonly KeyboardDevice Keyboard = new KeyboardDevice();
internal static IntPtr App { get; set; }
internal static string DisplayClassName;
public static bool UseDeferredRendering = true;
public static void Initialize()
{
Resolver.Resolve();
Native.GtkInit(0, IntPtr.Zero);
var disp = Native.GdkGetDefaultDisplay();
DisplayClassName = Utf8Buffer.StringFromPtr(Native.GTypeName(Marshal.ReadIntPtr(Marshal.ReadIntPtr(disp))));
using (var utf = new Utf8Buffer("avalonia.app." + Guid.NewGuid()))
App = Native.GtkApplicationNew(utf, 0);
//Mark current thread as UI thread

2
src/Gtk/Avalonia.Gtk3/IDeferredRenderOperation.cs

@ -4,6 +4,6 @@ namespace Avalonia.Gtk3
{
public interface IDeferredRenderOperation : IDisposable
{
void RenderNow();
void RenderNow(IntPtr? ctx);
}
}

11
src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs

@ -88,10 +88,10 @@ namespace Avalonia.Gtk3
private readonly int _width;
private readonly int _height;
public RenderOp(GtkWidget widget, ManagedCairoSurface _surface, double factor, int width, int height)
public RenderOp(GtkWidget widget, ManagedCairoSurface surface, double factor, int width, int height)
{
_widget = widget;
this._surface = _surface;
_surface = surface ?? throw new ArgumentNullException();
_factor = factor;
_width = width;
_height = height;
@ -103,9 +103,12 @@ namespace Avalonia.Gtk3
_surface = null;
}
public void RenderNow()
public void RenderNow(IntPtr? ctx)
{
DrawToWidget(_widget, _surface.Surface, _width, _height, _factor);
if(ctx.HasValue)
Draw(ctx.Value, _surface.Surface, _factor);
else
DrawToWidget(_widget, _surface.Surface, _width, _height, _factor);
}
}

4
src/Gtk/Avalonia.Gtk3/Interop/Native.cs

@ -318,6 +318,9 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
public delegate void g_object_ref(GObject instance);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
public delegate IntPtr g_type_name(IntPtr instance);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
public delegate ulong g_signal_connect_object(GObject instance, Utf8Buffer signal, IntPtr handler, IntPtr userData, int flags);
@ -410,6 +413,7 @@ namespace Avalonia.Gtk3.Interop
public static D.gtk_dialog_add_button GtkDialogAddButton;
public static D.g_object_unref GObjectUnref;
public static D.g_object_ref GObjectRef;
public static D.g_type_name GTypeName;
public static D.g_signal_connect_object GSignalConnectObject;
public static D.g_signal_handler_disconnect GSignalHandlerDisconnect;
public static D.g_timeout_add GTimeoutAdd;

5
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@ -31,7 +31,7 @@ namespace Avalonia.Gtk3
private object _lock = new object();
private IDeferredRenderOperation _nextRenderOperation;
private readonly AutoResetEvent _canSetNextOperation = new AutoResetEvent(true);
internal IntPtr? GdkWindowHandle;
public WindowBaseImpl(GtkWindow gtkWidget)
{
@ -55,6 +55,7 @@ namespace Avalonia.Gtk3
ConnectEvent("leave-notify-event", OnLeaveNotifyEvent);
Connect<Native.D.signal_generic>("destroy", OnDestroy);
Native.GtkWidgetRealize(gtkWidget);
GdkWindowHandle = this.Handle.Handle;
_lastSize = ClientSize;
if (Gtk3Platform.UseDeferredRendering)
{
@ -286,7 +287,7 @@ namespace Avalonia.Gtk3
}
if (op != null)
{
op?.RenderNow();
op?.RenderNow(null);
op?.Dispose();
}
}

54
src/Gtk/Avalonia.Gtk3/X11.cs

@ -0,0 +1,54 @@
using System;
using System.Runtime.InteropServices;
namespace Avalonia.Gtk3
{
class X11
{
[DllImport("libX11.so.6")]
public static extern IntPtr XOpenDisplay(IntPtr name);
[DllImport("libX11.so.6")]
public static extern IntPtr XFreeGC(IntPtr display, IntPtr gc);
[DllImport("libX11.so.6")]
public static extern IntPtr XCreateGC(IntPtr display, IntPtr drawable, ulong valuemask, IntPtr values);
[DllImport("libX11.so.6")]
public static extern int XInitImage(ref XImage image);
[DllImport("libX11.so.6")]
public static extern int XDestroyImage(ref XImage image);
[DllImport("libX11.so.6")]
public static extern IntPtr XSetErrorHandler(XErrorHandler handler);
public delegate int XErrorHandler(IntPtr display, IntPtr error);
[DllImport("libX11.so.6")]
public static extern int XPutImage(IntPtr display, IntPtr drawable, IntPtr gc, ref XImage image,
int srcx, int srcy, int destx, int desty, uint width, uint height);
public unsafe struct XImage
{
public int width, height; /* size of image */
public int xoffset; /* number of pixels offset in X direction */
public int format; /* XYBitmap, XYPixmap, ZPixmap */
public IntPtr data; /* pointer to image data */
public int byte_order; /* data byte order, LSBFirst, MSBFirst */
public int bitmap_unit; /* quant. of scanline 8, 16, 32 */
public int bitmap_bit_order; /* LSBFirst, MSBFirst */
public int bitmap_pad; /* 8, 16, 32 either XY or ZPixmap */
public int depth; /* depth of image */
public int bytes_per_line; /* accelerator to next scanline */
public int bits_per_pixel; /* bits per pixel (ZPixmap) */
public ulong red_mask; /* bits in z arrangement */
public ulong green_mask;
public ulong blue_mask;
private fixed byte funcs[128];
}
}
}

55
src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs

@ -0,0 +1,55 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.Platform;
namespace Avalonia.Gtk3
{
class X11Framebuffer : ILockedFramebuffer
{
private readonly IntPtr _display;
private readonly IntPtr _xid;
private IUnmanagedBlob _blob;
public X11Framebuffer(IntPtr display, IntPtr xid, int width, int height, int factor)
{
_display = display;
_xid = xid;
Width = width*factor;
Height = height*factor;
RowBytes = Width * 4;
Dpi = new Vector(96, 96) * factor;
Format = PixelFormat.Bgra8888;
_blob = AvaloniaLocator.Current.GetService<IRuntimePlatform>().AllocBlob(RowBytes * Height);
Address = _blob.Address;
}
public void Dispose()
{
var image = new X11.XImage();
int bitsPerPixel = 32;
image.width = Width;
image.height = Height;
image.format = 2; //ZPixmap;
image.data = Address;
image.byte_order = 0;// LSBFirst;
image.bitmap_unit = bitsPerPixel;
image.bitmap_bit_order = 0;// LSBFirst;
image.bitmap_pad = bitsPerPixel;
image.depth = 24;
image.bytes_per_line = RowBytes - Width * 4;
image.bits_per_pixel = bitsPerPixel;
X11.XInitImage(ref image);
var gc = X11.XCreateGC(_display, _xid, 0, IntPtr.Zero);
X11.XPutImage(_display, _xid, gc, ref image, 0, 0, 0, 0, (uint) Width, (uint) Height);
X11.XFreeGC(_display, gc);
_blob.Dispose();
}
public IntPtr Address { get; }
public int Width { get; }
public int Height { get; }
public int RowBytes { get; }
public Vector Dpi { get; }
public PixelFormat Format { get; }
}
}

1
src/Shared/PlatformSupport/StandardRuntimePlatform.cs

@ -76,6 +76,7 @@ namespace Avalonia.Shared.PlatformSupport
GC.RemoveMemoryPressure(Size);
IsDisposed = true;
Address = IntPtr.Zero;
Size = 0;
}
}

22
src/Skia/Avalonia.Skia/BitmapImpl.cs

@ -33,16 +33,22 @@ namespace Avalonia.Skia
PixelWidth = width;
_dpi = dpi;
var colorType = fmt?.ToSkColorType() ?? SKImageInfo.PlatformColorType;
var runtime = AvaloniaLocator.Current?.GetService<IRuntimePlatform>()?.GetRuntimeInfo();
var runtimePlatform = AvaloniaLocator.Current?.GetService<IRuntimePlatform>();
var runtime = runtimePlatform?.GetRuntimeInfo();
if (runtime?.IsDesktop == true && runtime?.OperatingSystem == OperatingSystemType.Linux)
colorType = SKColorType.Bgra8888;
Bitmap = new SKBitmap();
var nfo = new SKImageInfo(width, height, colorType, SKAlphaType.Premul);
var plat = AvaloniaLocator.Current.GetService<IRuntimePlatform>();
var blob = plat.AllocBlob(nfo.BytesSize);
Bitmap.InstallPixels(nfo, blob.Address, nfo.RowBytes, null, ReleaseDelegate, blob);
Bitmap = new SKBitmap(width, height, colorType, SKAlphaType.Premul);
if (runtimePlatform != null)
{
Bitmap = new SKBitmap();
var nfo = new SKImageInfo(width, height, colorType, SKAlphaType.Premul);
var plat = AvaloniaLocator.Current.GetService<IRuntimePlatform>();
var blob = plat.AllocBlob(nfo.BytesSize);
Bitmap.InstallPixels(nfo, blob.Address, nfo.RowBytes, null, ReleaseDelegate, blob);
}
else
Bitmap = new SKBitmap(width, height, colorType, SKAlphaType.Premul);
Bitmap.Erase(SKColor.Empty);
}

7
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -112,6 +112,7 @@ namespace Avalonia.Skia
public readonly SKPaint Paint;
private IDisposable _disposable1;
private IDisposable _disposable2;
public IDisposable ApplyTo(SKPaint paint)
{
@ -127,6 +128,8 @@ namespace Avalonia.Skia
{
if (_disposable1 == null)
_disposable1 = disposable;
else if (_disposable2 == null)
_disposable2 = disposable;
else
throw new InvalidOperationException();
}
@ -135,12 +138,14 @@ namespace Avalonia.Skia
{
Paint = paint;
_disposable1 = null;
_disposable2 = null;
}
public void Dispose()
{
Paint?.Dispose();
_disposable1?.Dispose();
_disposable2?.Dispose();
}
}
@ -221,8 +226,8 @@ namespace Avalonia.Skia
_visualBrushRenderer.RenderVisualBrush(ctx, visualBrush);
}
rv.AddDisposable(tileBrushImage);
tileBrushImage = intermediate;
rv.AddDisposable(tileBrushImage);
}
}
else

2
src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs

@ -146,6 +146,8 @@ namespace Avalonia.Direct2D1
}
if (s is IExternalDirect2DRenderTargetSurface external)
return new ExternalRenderTarget(external, s_dwfactory, s_imagingFactory);
if (s is IFramebufferPlatformSurface fb)
return new FramebufferShimRenderTarget(fb, s_imagingFactory, s_d2D1Factory, s_dwfactory);
}
throw new NotSupportedException("Don't know how to create a Direct2D1 renderer from any of provided surfaces");
}

84
src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs

@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Direct2D1.Media;
using Avalonia.Direct2D1.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Win32.Interop;
using SharpDX.Direct2D1;
using SharpDX.WIC;
using PixelFormat = Avalonia.Platform.PixelFormat;
namespace Avalonia.Direct2D1
{
class FramebufferShimRenderTarget : IRenderTarget
{
private readonly IFramebufferPlatformSurface _surface;
private readonly ImagingFactory _imagingFactory;
private readonly Factory _d2DFactory;
private readonly SharpDX.DirectWrite.Factory _dwriteFactory;
public FramebufferShimRenderTarget(IFramebufferPlatformSurface surface,
ImagingFactory imagingFactory, Factory d2dFactory, SharpDX.DirectWrite.Factory dwriteFactory)
{
_surface = surface;
_imagingFactory = imagingFactory;
_d2DFactory = d2dFactory;
_dwriteFactory = dwriteFactory;
}
public void Dispose()
{
}
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
{
var locked = _surface.Lock();
if (locked.Format == PixelFormat.Rgb565)
{
locked.Dispose();
throw new ArgumentException("Unsupported pixel format: " + locked.Format);
}
return new FramebufferShim(locked, _imagingFactory, _d2DFactory, _dwriteFactory)
.CreateDrawingContext(visualBrushRenderer);
}
class FramebufferShim : WicRenderTargetBitmapImpl
{
private readonly ILockedFramebuffer _target;
public FramebufferShim(ILockedFramebuffer target,
ImagingFactory imagingFactory, Factory d2dFactory, SharpDX.DirectWrite.Factory dwriteFactory
) : base(imagingFactory, d2dFactory, dwriteFactory,
target.Width, target.Height, target.Dpi.X, target.Dpi.Y, target.Format)
{
_target = target;
}
public override IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
{
return base.CreateDrawingContext(visualBrushRenderer, () =>
{
using (var l = WicImpl.Lock(BitmapLockFlags.Read))
{
for (var y = 0; y < _target.Height; y++)
{
UnmanagedMethods.CopyMemory(
_target.Address + _target.RowBytes * y,
l.Data.DataPointer + l.Stride * y,
(uint) Math.Min(l.Stride, _target.RowBytes));
}
}
Dispose();
_target.Dispose();
});
}
}
}
}

18
src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs

@ -22,8 +22,9 @@ namespace Avalonia.Direct2D1.Media
int width,
int height,
double dpiX,
double dpiY)
: base(imagingFactory, width, height)
double dpiY,
Platform.PixelFormat? pixelFormat = null)
: base(imagingFactory, width, height, pixelFormat)
{
var props = new RenderTargetProperties
{
@ -45,14 +46,13 @@ namespace Avalonia.Direct2D1.Media
base.Dispose();
}
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
public virtual IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
=> CreateDrawingContext(visualBrushRenderer, null);
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer, Action finishedCallback)
{
return new DrawingContextImpl(
visualBrushRenderer,
null,
_target,
_dwriteFactory,
WicImagingFactory);
return new DrawingContextImpl(visualBrushRenderer, null, _target, _dwriteFactory, WicImagingFactory,
finishedCallback: finishedCallback);
}
}
}

2
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs

@ -187,7 +187,7 @@ namespace Avalonia.Base.UnitTests
source.OnNext(45);
Assert.Equal(null, target.Foo);
Assert.Null(target.Foo);
}
[Fact]

2
tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs

@ -66,7 +66,7 @@ namespace Avalonia.Controls.UnitTests.Generators
target.Dematerialize(1, 1);
Assert.Equal(containers[0].ContainerControl, target.ContainerFromIndex(0));
Assert.Equal(null, target.ContainerFromIndex(1));
Assert.Null(target.ContainerFromIndex(1));
Assert.Equal(containers[2].ContainerControl, target.ContainerFromIndex(2));
}

4
tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs

@ -107,7 +107,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.Content = child;
target.Content = null;
Assert.Equal(null, child.GetLogicalParent());
Assert.Null(child.GetLogicalParent());
Assert.Empty(target.GetLogicalChildren());
}
@ -120,7 +120,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.Content = child;
target.Content = null;
Assert.Equal(null, child.GetVisualParent());
Assert.Null(child.GetVisualParent());
Assert.Empty(target.GetVisualChildren());
}

2
tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs

@ -191,7 +191,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.Content = "bar";
target.UpdateChild();
Assert.Equal(null, foo.Parent);
Assert.Null(foo.Parent);
logicalChildren = target.GetLogicalChildren();

8
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

@ -171,7 +171,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.SelectedItem = new Item();
Assert.Equal(null, target.SelectedItem);
Assert.Null(target.SelectedItem);
Assert.Equal(-1, target.SelectedIndex);
}
@ -278,7 +278,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.Items = null;
Assert.Equal(null, target.SelectedItem);
Assert.Null(target.SelectedItem);
Assert.Equal(-1, target.SelectedIndex);
}
@ -305,7 +305,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
items.RemoveAt(1);
Assert.Equal(null, target.SelectedItem);
Assert.Null(target.SelectedItem);
Assert.Equal(-1, target.SelectedIndex);
}
@ -334,7 +334,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
items.Clear();
Assert.Equal(null, target.SelectedItem);
Assert.Null(target.SelectedItem);
Assert.Equal(-1, target.SelectedIndex);
}

2
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs

@ -76,7 +76,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.SelectedItems = new AvaloniaList<object>();
Assert.Equal(-1, target.SelectedIndex);
Assert.Equal(null, target.SelectedItem);
Assert.Null(target.SelectedItem);
}
[Fact]

4
tests/Avalonia.Controls.UnitTests/StackPanelTests.cs

@ -70,7 +70,7 @@ namespace Avalonia.Controls.UnitTests
target.Measure(Size.Infinity);
target.Arrange(new Rect(target.DesiredSize));
Assert.Equal(new Size(120, 130), target.Bounds.Size);
Assert.Equal(new Size(120, 120), target.Bounds.Size);
Assert.Equal(new Rect(0, 0, 120, 20), target.Children[0].Bounds);
Assert.Equal(new Rect(0, 30, 120, 30), target.Children[1].Bounds);
Assert.Equal(new Rect(0, 70, 120, 50), target.Children[2].Bounds);
@ -94,7 +94,7 @@ namespace Avalonia.Controls.UnitTests
target.Measure(Size.Infinity);
target.Arrange(new Rect(target.DesiredSize));
Assert.Equal(new Size(130, 120), target.Bounds.Size);
Assert.Equal(new Size(120, 120), target.Bounds.Size);
Assert.Equal(new Rect(0, 0, 20, 120), target.Children[0].Bounds);
Assert.Equal(new Rect(30, 0, 30, 120), target.Children[1].Bounds);
Assert.Equal(new Rect(70, 0, 50, 120), target.Children[2].Bounds);

2
tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_RelativeSource.cs

@ -117,7 +117,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
};
target.Bind(TextBox.TextProperty, binding);
Assert.Equal(null, target.Text);
Assert.Null(target.Text);
}
[Fact]

4
tests/Avalonia.Markup.Xaml.UnitTests/Templates/MemberSelectorTests.cs

@ -63,7 +63,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Templates
{
var selector = new MemberSelector() { MemberName = "StringValue" };
Assert.Equal(null, selector.Select(null));
Assert.Null(selector.Select(null));
}
[Fact]
@ -73,7 +73,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Templates
var data = new Item() { StringValue = "Value1" };
Assert.Same(null, selector.Select(data));
Assert.Null(selector.Select(data));
}
[Fact]

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

@ -64,13 +64,13 @@ namespace Avalonia.Direct2D1.RenderTests.Media
public void Deallocate() => Marshal.FreeHGlobal(Address);
}
#if AVALONIA_SKIA
[Theory]
#else
[Theory(Skip = "Framebuffer not supported")]
[InlineData(PixelFormat.Rgba8888), InlineData(PixelFormat.Bgra8888),
#if SKIA
InlineData(PixelFormat.Rgb565)
#endif
[InlineData(PixelFormat.Rgba8888), InlineData(PixelFormat.Bgra8888), InlineData(PixelFormat.Rgb565)]
]
public void FramebufferRenderResultsShouldBeUsableAsBitmap(PixelFormat fmt)
{
var testName = nameof(FramebufferRenderResultsShouldBeUsableAsBitmap) + "_" + fmt;
@ -84,6 +84,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media
ctx.FillRectangle(Brushes.Chartreuse, new Rect(0, 0, 20, 100));
ctx.FillRectangle(Brushes.Crimson, new Rect(20, 0, 20, 100));
ctx.FillRectangle(Brushes.Gold, new Rect(40, 0, 20, 100));
ctx.PopOpacity();
}
var bmp = new Bitmap(fmt, fb.Address, fb.Width, fb.Height, fb.RowBytes);

55
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs

@ -0,0 +1,55 @@
using System;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Xunit;
namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
{
public class DrawOperationTests
{
[Fact]
public void Empty_Bounds_Remain_Empty()
{
var target = new TestDrawOperation(Rect.Empty, Matrix.Identity, null);
Assert.Equal(Rect.Empty, target.Bounds);
}
[Theory]
[InlineData(10, 10, 10, 10, 1, 1, 1, 9, 9, 12, 12)]
[InlineData(10, 10, 10, 10, 1, 1, 2, 9, 9, 12, 12)]
[InlineData(10, 10, 10, 10, 1.5, 1.5, 1, 14, 14, 17, 17)]
public void Rectangle_Bounds_Are_Snapped_To_Pixels(
double x,
double y,
double width,
double height,
double scaleX,
double scaleY,
double? penThickness,
double expectedX,
double expectedY,
double expectedWidth,
double expectedHeight)
{
var target = new TestDrawOperation(
new Rect(x, y, width, height),
Matrix.CreateScale(scaleX, scaleY),
penThickness.HasValue ? new Pen(Brushes.Black, penThickness.Value) : null);
Assert.Equal(new Rect(expectedX, expectedY, expectedWidth, expectedHeight), target.Bounds);
}
private class TestDrawOperation : DrawOperation
{
public TestDrawOperation(Rect bounds, Matrix transform, Pen pen)
:base(bounds, transform, pen)
{
}
public override bool HitTest(Point p) => false;
public override void Render(IDrawingContextImpl context) { }
}
}
}

BIN
tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Bgra8888.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 800 B

BIN
tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Rgb565.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 B

BIN
tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Rgba8888.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 800 B

Loading…
Cancel
Save