Conflicts: src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs tests/Avalonia.RenderTests/Avalonia.RenderTests.projitems tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.csscenegraph-after-breakage
@ -1,15 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Avalonia.Controls.Platform.Surfaces |
|||
{ |
|||
public enum PixelFormat |
|||
{ |
|||
Rgb565, |
|||
Rgba8888, |
|||
Bgra8888 |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.Media.Imaging |
|||
{ |
|||
/// <summary>
|
|||
/// Holds a writable bitmap image.
|
|||
/// </summary>
|
|||
public class WritableBitmap : Bitmap |
|||
{ |
|||
public WritableBitmap(int width, int height, PixelFormat? format = null) |
|||
: base(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>().CreateWritableBitmap(width, height, format)) |
|||
{ |
|||
} |
|||
|
|||
public ILockedFramebuffer Lock() => ((IWritableBitmapImpl) PlatformImpl).Lock(); |
|||
} |
|||
} |
|||
@ -1,6 +1,6 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Controls.Platform.Surfaces |
|||
namespace Avalonia.Platform |
|||
{ |
|||
public interface ILockedFramebuffer : IDisposable |
|||
{ |
|||
@ -0,0 +1,16 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Avalonia.Platform |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the platform-specific interface for a <see cref="Avalonia.Media.Imaging.WritableBitmap"/>.
|
|||
/// </summary>
|
|||
public interface IWritableBitmapImpl : IBitmapImpl |
|||
{ |
|||
ILockedFramebuffer Lock(); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace Avalonia.Platform |
|||
{ |
|||
public enum PixelFormat |
|||
{ |
|||
Rgb565, |
|||
Rgba8888, |
|||
Bgra8888 |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Platform; |
|||
using SharpDX.WIC; |
|||
using PixelFormat = Avalonia.Platform.PixelFormat; |
|||
|
|||
namespace Avalonia.Direct2D1.Media.Imaging |
|||
{ |
|||
class WritableWicBitmapImpl : WicBitmapImpl, IWritableBitmapImpl |
|||
{ |
|||
public WritableWicBitmapImpl(ImagingFactory factory, int width, int height, PixelFormat? pixelFormat) |
|||
: base(factory, width, height, pixelFormat) |
|||
{ |
|||
} |
|||
|
|||
class LockedBitmap : ILockedFramebuffer |
|||
{ |
|||
private readonly BitmapLock _lock; |
|||
private readonly PixelFormat _format; |
|||
|
|||
public LockedBitmap(BitmapLock l, PixelFormat format) |
|||
{ |
|||
_lock = l; |
|||
_format = format; |
|||
} |
|||
|
|||
|
|||
public void Dispose() |
|||
{ |
|||
_lock.Dispose(); |
|||
} |
|||
|
|||
public IntPtr Address => _lock.Data.DataPointer; |
|||
public int Width => _lock.Size.Width; |
|||
public int Height => _lock.Size.Height; |
|||
public int RowBytes => _lock.Stride; |
|||
public Size Dpi { get; } = new Size(96, 96); |
|||
public PixelFormat Format => _format; |
|||
|
|||
} |
|||
|
|||
public ILockedFramebuffer Lock() => new LockedBitmap(WicImpl.Lock(BitmapLockFlags.Write), PixelFormat.Value); |
|||
} |
|||
} |
|||
@ -0,0 +1,139 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.IO; |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Controls.Platform.Surfaces; |
|||
using Avalonia.Controls.Shapes; |
|||
using Avalonia.Layout; |
|||
using Avalonia.Media; |
|||
using Avalonia.Media.Imaging; |
|||
using Avalonia.Platform; |
|||
using Xunit; |
|||
|
|||
#if AVALONIA_CAIRO
|
|||
namespace Avalonia.Cairo.RenderTests.Media |
|||
#elif AVALONIA_SKIA
|
|||
namespace Avalonia.Skia.RenderTests |
|||
#else
|
|||
namespace Avalonia.Direct2D1.RenderTests.Media |
|||
#endif
|
|||
{ |
|||
public class BitmapTests : TestBase |
|||
{ |
|||
public BitmapTests() |
|||
: base(@"Media\Bitmap") |
|||
{ |
|||
Directory.CreateDirectory(OutputPath); |
|||
} |
|||
|
|||
class Framebuffer : ILockedFramebuffer, IFramebufferPlatformSurface |
|||
{ |
|||
public Framebuffer(PixelFormat fmt, int width, int height) |
|||
{ |
|||
Format = fmt; |
|||
var bpp = fmt == PixelFormat.Rgb565 ? 2 : 4; |
|||
Width = width; |
|||
Height = height; |
|||
RowBytes = bpp * width; |
|||
Address = Marshal.AllocHGlobal(Height * RowBytes); |
|||
} |
|||
|
|||
public IntPtr Address { get; } |
|||
|
|||
public Size Dpi { get; } = new Size(96, 96); |
|||
|
|||
public PixelFormat Format { get; } |
|||
|
|||
public int Height { get; } |
|||
|
|||
public int RowBytes { get; } |
|||
|
|||
public int Width { get; } |
|||
|
|||
public void Dispose() |
|||
{ |
|||
//no-op
|
|||
} |
|||
|
|||
public ILockedFramebuffer Lock() |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
public void Deallocate() => Marshal.FreeHGlobal(Address); |
|||
} |
|||
|
|||
|
|||
#if AVALONIA_SKIA
|
|||
[Theory] |
|||
#else
|
|||
[Theory(Skip = "Framebuffer not supported")] |
|||
#endif
|
|||
[InlineData(PixelFormat.Rgba8888), InlineData(PixelFormat.Bgra8888), InlineData(PixelFormat.Rgb565)] |
|||
public void FramebufferRenderResultsShouldBeUsableAsBitmap(PixelFormat fmt) |
|||
{ |
|||
var testName = nameof(FramebufferRenderResultsShouldBeUsableAsBitmap) + "_" + fmt; |
|||
var fb = new Framebuffer(fmt, 80, 80); |
|||
var r = Avalonia.AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); |
|||
using (var target = r.CreateRenderTarget(new object[] { fb })) |
|||
using (var ctx = target.CreateDrawingContext(null)) |
|||
{ |
|||
ctx.PushOpacity(0.8); |
|||
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)); |
|||
} |
|||
|
|||
var bmp = new Bitmap(fmt, fb.Address, fb.Width, fb.Height, fb.RowBytes); |
|||
fb.Deallocate(); |
|||
using (var rtb = new RenderTargetBitmap(100, 100)) |
|||
{ |
|||
using (var ctx = rtb.CreateDrawingContext(null)) |
|||
{ |
|||
ctx.FillRectangle(Brushes.Blue, new Rect(0, 0, 100, 100)); |
|||
ctx.FillRectangle(Brushes.Pink, new Rect(0, 20, 100, 10)); |
|||
|
|||
var rc = new Rect(0, 0, 60, 60); |
|||
ctx.DrawImage(bmp.PlatformImpl, 1, rc, rc); |
|||
} |
|||
rtb.Save(System.IO.Path.Combine(OutputPath, testName + ".out.png")); |
|||
} |
|||
CompareImagesNoRenderer(testName); |
|||
} |
|||
|
|||
#if AVALONIA_CAIRO
|
|||
//wontfix
|
|||
#else
|
|||
[Theory] |
|||
#endif
|
|||
[InlineData(PixelFormat.Bgra8888), InlineData(PixelFormat.Rgba8888)] |
|||
public void WritableBitmapShouldBeUsable(PixelFormat fmt) |
|||
{ |
|||
var writableBitmap = new WritableBitmap(256, 256, fmt); |
|||
|
|||
var data = new int[256 * 256]; |
|||
for (int y = 0; y < 256; y++) |
|||
for (int x = 0; x < 256; x++) |
|||
data[y * 256 + x] =(int)((uint)(x + (y << 8)) | 0xFF000000u); |
|||
|
|||
|
|||
using (var l = writableBitmap.Lock()) |
|||
{ |
|||
for(var r = 0; r<256; r++) |
|||
{ |
|||
Marshal.Copy(data, r * 256, new IntPtr(l.Address.ToInt64() + r * l.RowBytes), 256); |
|||
} |
|||
} |
|||
|
|||
|
|||
var name = nameof(WritableBitmapShouldBeUsable) + "_" + fmt; |
|||
|
|||
writableBitmap.Save(System.IO.Path.Combine(OutputPath, name + ".out.png")); |
|||
CompareImagesNoRenderer(testName); |
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,174 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using Avalonia.Media; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.Visuals.UnitTests.VisualTree |
|||
{ |
|||
class MockRenderInterface : IPlatformRenderInterface |
|||
{ |
|||
public IFormattedTextImpl CreateFormattedText( |
|||
string text, |
|||
string fontFamilyName, |
|||
double fontSize, |
|||
FontStyle fontStyle, |
|||
TextAlignment textAlignment, |
|||
FontWeight fontWeight, |
|||
TextWrapping wrapping) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public IStreamGeometryImpl CreateStreamGeometry() |
|||
{ |
|||
return new MockStreamGeometry(); |
|||
} |
|||
|
|||
public IBitmapImpl LoadBitmap(Stream stream) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public IBitmapImpl LoadBitmap(string fileName) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public IBitmapImpl LoadBitmap(PixelFormat format, IntPtr data, int width, int height, int stride) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public IWritableBitmapImpl CreateWritableBitmap(int width, int height, PixelFormat? fmt) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
class MockStreamGeometry : IStreamGeometryImpl |
|||
{ |
|||
private MockStreamGeometryContext _impl = new MockStreamGeometryContext(); |
|||
public Rect Bounds |
|||
{ |
|||
get |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
} |
|||
|
|||
public Matrix Transform |
|||
{ |
|||
get |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
set |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
} |
|||
|
|||
public IStreamGeometryImpl Clone() |
|||
{ |
|||
return this; |
|||
} |
|||
|
|||
public bool FillContains(Point point) |
|||
{ |
|||
return _impl.FillContains(point); |
|||
} |
|||
|
|||
public Rect GetRenderBounds(double strokeThickness) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public IStreamGeometryContextImpl Open() |
|||
{ |
|||
return _impl; |
|||
} |
|||
|
|||
class MockStreamGeometryContext : IStreamGeometryContextImpl |
|||
{ |
|||
private List<Point> points = new List<Point>(); |
|||
public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public void BeginFigure(Point startPoint, bool isFilled) |
|||
{ |
|||
points.Add(startPoint); |
|||
} |
|||
|
|||
public void CubicBezierTo(Point point1, Point point2, Point point3) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
} |
|||
|
|||
public void EndFigure(bool isClosed) |
|||
{ |
|||
} |
|||
|
|||
public void LineTo(Point point) |
|||
{ |
|||
points.Add(point); |
|||
} |
|||
|
|||
public void QuadraticBezierTo(Point control, Point endPoint) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public void SetFillRule(FillRule fillRule) |
|||
{ |
|||
} |
|||
|
|||
public bool FillContains(Point point) |
|||
{ |
|||
// Use the algorithm from http://www.blackpawn.com/texts/pointinpoly/default.html
|
|||
// to determine if the point is in the geometry (since it will always be convex in this situation)
|
|||
for (int i = 0; i < points.Count; i++) |
|||
{ |
|||
var a = points[i]; |
|||
var b = points[(i + 1) % points.Count]; |
|||
var c = points[(i + 2) % points.Count]; |
|||
|
|||
Vector v0 = c - a; |
|||
Vector v1 = b - a; |
|||
Vector v2 = point - a; |
|||
|
|||
var dot00 = v0 * v0; |
|||
var dot01 = v0 * v1; |
|||
var dot02 = v0 * v2; |
|||
var dot11 = v1 * v1; |
|||
var dot12 = v1 * v2; |
|||
|
|||
|
|||
var invDenom = 1 / (dot00 * dot11 - dot01 * dot01); |
|||
var u = (dot11 * dot02 - dot01 * dot12) * invDenom; |
|||
var v = (dot00 * dot12 - dot01 * dot02) * invDenom; |
|||
if ((u >= 0) && (v >= 0) && (u + v < 1)) return true; |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 800 B |
|
After Width: | Height: | Size: 786 B |
|
After Width: | Height: | Size: 800 B |
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 89 KiB |