Browse Source

Opacity masks on Skia now work correctly. Implemented via skia layers.

Jeremy Koritzinsky 9 years ago
parent
commit
6feb0f7d88

+ 12 - 44
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@@ -10,16 +10,14 @@ namespace Avalonia.Skia
 {
     internal class DrawingContextImpl : IDrawingContextImpl
     {
-        private Stack<SKSurface> surfaceStack = new Stack<SKSurface>();
-        private Stack<MaskWrapper> maskStack = new Stack<MaskWrapper>();
-        private SKCanvas initialCanvas;
-
-        public SKCanvas CurrentCanvas => surfaceStack.Count == 0 ? initialCanvas : surfaceStack.Peek().Canvas;
+        private Stack<PaintWrapper> maskStack = new Stack<PaintWrapper>();
+        
+        public SKCanvas CurrentCanvas { get; private set; }
 
         public DrawingContextImpl(SKCanvas canvas)
         {
-            initialCanvas = canvas;
-            initialCanvas.Clear();
+            CurrentCanvas = canvas;
+            CurrentCanvas.Clear();
         }
 
         public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect)
@@ -57,18 +55,6 @@ namespace Avalonia.Skia
             }
         }
 
-        struct MaskWrapper : IDisposable
-        {
-            public PaintWrapper Mask { get; set; }
-            public Rect Bounds { get; set; }
-
-            public void Dispose()
-            {
-                Mask.Dispose();
-            }
-        }
-
-
         struct PaintWrapper : IDisposable
         {
             //We are saving memory allocations there
@@ -317,37 +303,19 @@ namespace Avalonia.Skia
 
         public void PushOpacityMask(IBrush mask, Rect bounds)
         {
-            surfaceStack.Push(SKSurface.Create((int)bounds.Width, (int)bounds.Height, SKColorType.N_32, SKAlphaType.Premul));
-            surfaceStack.Peek().Canvas.Clear();
-            var paint = new MaskWrapper { Mask = CreatePaint(mask, bounds.Size), Bounds = bounds };
-            maskStack.Push(paint);
+            CurrentCanvas.SaveLayer(new SKPaint());
+            maskStack.Push(CreatePaint(mask, bounds.Size));
         }
 
         public void PopOpacityMask()
         {
-            using (var surface = surfaceStack.Pop())
-            using (var mask = maskStack.Pop())
-            using (var combindingPaint = new SKPaint())
-            using (var surfaceImage = surface.Snapshot())
+            CurrentCanvas.SaveLayer(new SKPaint { XferMode = SKXferMode.DstIn });
+            using (var paintWrapper = maskStack.Pop())
             {
-                using (var maskSurface = SKSurface.Create((int)mask.Bounds.Width, (int)mask.Bounds.Height, SKColorType.N_32, SKAlphaType.Premul))
-                {
-                    maskSurface.Canvas.Clear(SKColors.Transparent);
-                    maskSurface.Canvas.DrawRect(SKRect.Create((float)mask.Bounds.Width, (float)mask.Bounds.Height), mask.Mask.Paint);
-                    using (var maskImage = maskSurface.Snapshot())
-                    using (var combindingSurface = SKSurface.Create((int)mask.Bounds.Width, (int)mask.Bounds.Height, SKColorType.N_32, SKAlphaType.Premul))
-                    {
-                        combindingSurface.Canvas.Clear(SKColors.Transparent);
-                        combindingSurface.Canvas.DrawImage(surfaceImage, 0, 0, combindingPaint);
-                        combindingPaint.XferMode = SKXferMode.DstIn;
-                        combindingSurface.Canvas.DrawImage(maskImage, 0, 0, combindingPaint);
-                        using (var maskedImage = combindingSurface.Snapshot())
-                        {
-                            CurrentCanvas.DrawImage(maskedImage, mask.Bounds.ToSKRect());
-                        }
-                    } 
-                }
+                CurrentCanvas.DrawPaint(paintWrapper.Paint);
             }
+            CurrentCanvas.Restore();
+            CurrentCanvas.Restore();
         }
 
         private Matrix _currentTransform = Matrix.Identity;

+ 0 - 4
tests/Avalonia.RenderTests/OpacityMaskTests.cs

@@ -21,11 +21,7 @@ namespace Avalonia.Direct2D1.RenderTests
         {
         }
 
-#if AVALONIA_SKIA
-        [Fact(Skip = "Opacity Masks on Skia are currently bugged.")]
-#else
         [Fact]
-#endif
         public void Opacity_Mask_Masks_Element()
         {
             var target = new Canvas

BIN
tests/TestFiles/Skia/OpacityMask/Opacity_Mask_Masks_Element.expected.png