From 6feb0f7d883566bc79d3b8e0f1375fd822ef2d10 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sat, 25 Jun 2016 14:08:59 -0400 Subject: [PATCH] Opacity masks on Skia now work correctly. Implemented via skia layers. --- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 56 ++++-------------- .../Avalonia.RenderTests/OpacityMaskTests.cs | 4 -- .../Opacity_Mask_Masks_Element.expected.png | Bin 846 -> 732 bytes 3 files changed, 12 insertions(+), 48 deletions(-) diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 4ae306ff16..02aae6d7bc 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -10,16 +10,14 @@ namespace Avalonia.Skia { internal class DrawingContextImpl : IDrawingContextImpl { - private Stack surfaceStack = new Stack(); - private Stack maskStack = new Stack(); - private SKCanvas initialCanvas; - - public SKCanvas CurrentCanvas => surfaceStack.Count == 0 ? initialCanvas : surfaceStack.Peek().Canvas; + private Stack maskStack = new Stack(); + + 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; diff --git a/tests/Avalonia.RenderTests/OpacityMaskTests.cs b/tests/Avalonia.RenderTests/OpacityMaskTests.cs index 903181c763..b23f231f2e 100644 --- a/tests/Avalonia.RenderTests/OpacityMaskTests.cs +++ b/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 diff --git a/tests/TestFiles/Skia/OpacityMask/Opacity_Mask_Masks_Element.expected.png b/tests/TestFiles/Skia/OpacityMask/Opacity_Mask_Masks_Element.expected.png index f57c675b7a013cb3fe4f94241527f96150ca10b6..935ba57aff1ca7b5292d8c79926015c4d985c665 100644 GIT binary patch delta 708 zcmV;#0z3WA2HXXZB!2;OQb$4nuFf3k00004XF*Lt006O%3;baP00009a7bBm000id z000id0mpBsWB>pHaY;l$RCr$Pnb}KLQ4~klTf=knT+~w~zLgIW6;V-Ck{FVeCWa}Y zh$0S%Wr;YTI3S87qKG1j_!suu;0Hbio@3nIUaaTua@O779e>Ux2!bF8f*=TjAP9mW z2!bFe8h7O$?Crwt4(x2h_7-ey!sZ5Sti$>mtgXW83al){@^ARP1WSvsxBv_DFh2)# zw?I1QmrN0B7G}SJbj%%@D%LbiPr=kpkWTq2Q^uNv$*&+CV$+p6)&xw9!PpIu&iEm7 z#2SUsFCZOpTYu(?H3B2UFnk@P6TZuwvFy(K8Ke`g$rfm_C^=#c!H}I4JI?+geQ;7% zLZvM-b*w=c{0P!1S7akJ&cIBHSOYN75B>JL`-kw!30VOZeb9HGtR`rB0kWXn zlSdiLUcJtNEHHNGvM@&x>p4gVT#&W{n=@7eG!%Zi4su-Df^)@s2GSW8%@&Pry!m4N~VZqyRNRM>zFKqGFxARS-}%<<@OFxF#`POuNGIb+pAt()m8@k!>2 z^$4UhuF4#->x0AdO(|=JGmW#+`7e{vVOLBqYmA>v$bW%QVe{_V5F=}b(~UEc zH7pa|Vb6$@HGjrW1lO=Yw6OPa6@QA6HN&aK{lRtIB4u!G9K%ICj#H+SHO4mv-pQyX zZPFiUoW^6=#8Iou8sm2dwxaV-7dA5Qx=mcdNvp}4VQb*M=)5zxMhgu5flu(Y)nzrD zY@CSBdu_rRci;tmVqaC(7+;Fcdo98mci}}mVmet3Cx2T1c!!ZLtZ@%s);G^n${OQG zBlAxcHvjLxf_uftYB(ObmZb@6JcyTYw>Vj2d?Biitn;ujpGruWE}5Y z#&fvc;RXF4Sz~-YqMjoWZ@R>w7cv%e(85>U0fZ3VVFMrHEqwzX`7vCMKaHp-qr(hb zZ~Ttuaet?HSw9VIN7Rz`lpOdLuj5y-vS#=!s*cn-Y~Uu|z>nf&{T$U@tYOk0_ziF3 zJJZW*_&hTIwd^J5Clznw3)9L9Tf0WqdA#%2zz6yf&lIv|xE`H%%2V>+X#L(BRzEx( zPR)0CE*z3s{-dzL>n;)}W(0?j$)U*ry z?)UmKvX0$Eci2<@xkgPbYiD35xQ<&yKP5jBE34_FNgAQT2ENySDHtVdJF~mUnZvG% zku}3t3HdLOalEsozowRTbKsZg{Flj^^glMOteO4=a5^c%27b_AhZXc+vUb+dUF7a1 zuP`}WXYF