diff --git a/src/Shared/RenderHelpers/TileBrushImplHelper.cs b/src/Shared/RenderHelpers/TileBrushImplHelper.cs index fc0057f01b..d1d3073be6 100644 --- a/src/Shared/RenderHelpers/TileBrushImplHelper.cs +++ b/src/Shared/RenderHelpers/TileBrushImplHelper.cs @@ -17,17 +17,17 @@ namespace Avalonia.RenderHelpers private readonly Vector _scale; private readonly Vector _translate; private readonly Size _imageSize; - private readonly VisualBrush _visualBrush; - private readonly ImageBrush _imageBrush; + private readonly IVisualBrush _visualBrush; + private readonly IImageBrush _imageBrush; private readonly Matrix _transform; private readonly Rect _drawRect; public bool IsValid { get; } - public TileBrushImplHelper(TileBrush brush, Size targetSize) + public TileBrushImplHelper(ITileBrush brush, Size targetSize) { - _imageBrush = brush as ImageBrush; - _visualBrush = brush as VisualBrush; + _imageBrush = brush as IImageBrush; + _visualBrush = brush as IVisualBrush; if (_imageBrush != null) { if (_imageBrush.Source == null) @@ -115,7 +115,7 @@ namespace Avalonia.RenderHelpers /// A vector with the X and Y _translate. public static Vector CalculateTranslate( - TileBrush brush, + ITileBrush brush, Rect sourceRect, Rect destinationRect, Vector scale) diff --git a/src/Skia/Avalonia.Skia/BitmapImpl.cs b/src/Skia/Avalonia.Skia/BitmapImpl.cs index 4d6b106fc6..7926a62a50 100644 --- a/src/Skia/Avalonia.Skia/BitmapImpl.cs +++ b/src/Skia/Avalonia.Skia/BitmapImpl.cs @@ -30,6 +30,11 @@ namespace Avalonia.Skia if (runtime?.IsDesktop == true && runtime?.OperatingSystem == OperatingSystemType.Linux) colorType = SKColorType.Bgra8888; Bitmap = new SKBitmap(width, height, colorType, SKAlphaType.Premul); + + using (var context = new BitmapDrawingContext(Bitmap, null)) + { + context.Clear(Colors.Transparent); + } } public void Dispose() @@ -56,7 +61,8 @@ namespace Avalonia.Skia { private readonly SKSurface _surface; - public BitmapDrawingContext(SKBitmap bitmap) : this(CreateSurface(bitmap)) + public BitmapDrawingContext(SKBitmap bitmap, IVisualBrushRenderer visualBrushRenderer) + : this(CreateSurface(bitmap), visualBrushRenderer) { } @@ -70,7 +76,8 @@ namespace Avalonia.Skia return rv; } - public BitmapDrawingContext(SKSurface surface) : base(surface.Canvas) + public BitmapDrawingContext(SKSurface surface, IVisualBrushRenderer visualBrushRenderer) + : base(surface.Canvas, visualBrushRenderer) { _surface = surface; } @@ -84,8 +91,7 @@ namespace Avalonia.Skia public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) { - - return new BitmapDrawingContext(Bitmap); + return new BitmapDrawingContext(Bitmap, visualBrushRenderer); } public void Save(Stream stream) diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index eacf64e550..4be8662a18 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -6,6 +6,8 @@ using System; using System.Collections.Generic; using System.Linq; using Avalonia.Platform; +using Avalonia.Rendering.Utilities; +using Avalonia.Rendering; namespace Avalonia.Skia { @@ -13,14 +15,20 @@ namespace Avalonia.Skia { private readonly Matrix? _postTransform; private readonly IDisposable[] _disposables; + private readonly IVisualBrushRenderer _visualBrushRenderer; private Stack maskStack = new Stack(); public SKCanvas Canvas { get; private set; } - public DrawingContextImpl(SKCanvas canvas, Matrix? postTransform = null, params IDisposable[] disposables) + public DrawingContextImpl( + SKCanvas canvas, + IVisualBrushRenderer visualBrushRenderer, + Matrix? postTransform = null, + params IDisposable[] disposables) { if (postTransform.HasValue && !postTransform.Value.IsIdentity) _postTransform = postTransform; + _visualBrushRenderer = visualBrushRenderer; _disposables = disposables; Canvas = canvas; Transform = Matrix.Identity; @@ -160,14 +168,14 @@ namespace Avalonia.Skia return rv; } - var gradient = brush as GradientBrush; + var gradient = brush as IGradientBrush; if (gradient != null) { var tileMode = gradient.SpreadMethod.ToSKShaderTileMode(); var stopColors = gradient.GradientStops.Select(s => s.Color.ToSKColor()).ToArray(); var stopOffsets = gradient.GradientStops.Select(s => (float)s.Offset).ToArray(); - var linearGradient = brush as LinearGradientBrush; + var linearGradient = brush as ILinearGradientBrush; if (linearGradient != null) { var start = linearGradient.StartPoint.ToPixels(targetSize).ToSKPoint(); @@ -180,7 +188,7 @@ namespace Avalonia.Skia } else { - var radialGradient = brush as RadialGradientBrush; + var radialGradient = brush as IRadialGradientBrush; if (radialGradient != null) { var center = radialGradient.Center.ToPixels(targetSize).ToSKPoint(); @@ -199,15 +207,57 @@ namespace Avalonia.Skia return rv; } - var tileBrush = brush as TileBrush; - if (tileBrush != null) + var tileBrush = brush as ITileBrush; + var visualBrush = brush as IVisualBrush; + var tileBrushImage = default(BitmapImpl); + + if (visualBrush != null) { - var helper = new TileBrushImplHelper(tileBrush, targetSize); - var bitmap = new BitmapImpl((int)helper.IntermediateSize.Width, (int)helper.IntermediateSize.Height); + if (_visualBrushRenderer != null) + { + var intermediateSize = _visualBrushRenderer.GetRenderTargetSize(visualBrush); + + if (intermediateSize.Width >= 1 && intermediateSize.Height >= 1) + { + var intermediate = new BitmapImpl((int)intermediateSize.Width, (int)intermediateSize.Height); + + using (var ctx = intermediate.CreateDrawingContext(_visualBrushRenderer)) + { + ctx.Clear(Colors.Transparent); + _visualBrushRenderer.RenderVisualBrush(ctx, visualBrush); + } + + rv.AddDisposable(tileBrushImage); + tileBrushImage = intermediate; + } + } + else + { + throw new NotSupportedException("No IVisualBrushRenderer was supplied to DrawingContextImpl."); + } + } + else + { + tileBrushImage = (BitmapImpl)((tileBrush as IImageBrush)?.Source?.PlatformImpl); + } + + if (tileBrush != null && tileBrushImage != null) + { + var calc = new TileBrushCalculator(tileBrush, new Size(tileBrushImage.PixelWidth, tileBrushImage.PixelHeight), targetSize); + var bitmap = new BitmapImpl((int)calc.IntermediateSize.Width, (int)calc.IntermediateSize.Height); rv.AddDisposable(bitmap); - using (var ctx = bitmap.CreateDrawingContext(null)) - helper.DrawIntermediate(new DrawingContext(ctx)); - SKMatrix translation = SKMatrix.MakeTranslation(-(float)helper.DestinationRect.X, -(float)helper.DestinationRect.Y); + using (var context = bitmap.CreateDrawingContext(null)) + { + var rect = new Rect(0, 0, tileBrushImage.PixelWidth, tileBrushImage.PixelHeight); + + context.Clear(Colors.Transparent); + context.PushClip(calc.IntermediateClip); + context.Transform = calc.IntermediateTransform; + context.DrawImage(tileBrushImage, 1, rect, rect); + context.PopClip(); + } + + SKMatrix translation = SKMatrix.MakeTranslation(-(float)calc.DestinationRect.X, -(float)calc.DestinationRect.Y); SKShaderTileMode tileX = tileBrush.TileMode == TileMode.None ? SKShaderTileMode.Clamp diff --git a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs index 7eaa8cf172..287c056cc9 100644 --- a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs @@ -88,10 +88,9 @@ namespace Avalonia.Skia canvas.RestoreToCount(0); canvas.Save(); - canvas.Clear(SKColors.Red); canvas.ResetMatrix(); var scale = Matrix.CreateScale(fb.Dpi.Width / 96, fb.Dpi.Height / 96); - return new DrawingContextImpl(canvas, scale, canvas, surface, shim, fb); + return new DrawingContextImpl(canvas, visualBrushRenderer, scale, canvas, surface, shim, fb); } } } diff --git a/tests/TestFiles/Skia/GeometryClipping/Geometry_Clip_Clips_Path.expected.png b/tests/TestFiles/Skia/GeometryClipping/Geometry_Clip_Clips_Path.expected.png index 892899507b..5993088a33 100644 Binary files a/tests/TestFiles/Skia/GeometryClipping/Geometry_Clip_Clips_Path.expected.png and b/tests/TestFiles/Skia/GeometryClipping/Geometry_Clip_Clips_Path.expected.png differ