From 4dbf646a734bc31df2270f6a638acf2d31b0d783 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 10 Sep 2021 22:40:43 +0200 Subject: [PATCH] Implement CombinedGeometry. --- .../HeadlessPlatformRenderInterface.cs | 1 + .../Media/CombinedGeometry.cs | 170 ++++++++++++++++++ src/Avalonia.Visuals/Media/GeometryGroup.cs | 2 +- .../Platform/IPlatformRenderInterface.cs | 9 + .../Avalonia.Skia/CombinedGeometryImpl.cs | 35 ++++ .../Avalonia.Skia/PlatformRenderInterface.cs | 5 + .../Avalonia.Direct2D1/Direct2D1Platform.cs | 1 + .../Media/CombinedGeometryImpl.cs | 36 ++++ .../NullRenderingPlatform.cs | 5 + .../Media/CombinedGeometryTests.cs | 56 ++++++ .../MockPlatformRenderInterface.cs | 5 + .../VisualTree/MockRenderInterface.cs | 5 + .../GeometryCombineMode_Exclude.expected.png | Bin 0 -> 1691 bytes ...GeometryCombineMode_Intersect.expected.png | Bin 0 -> 1618 bytes .../GeometryCombineMode_Union.expected.png | Bin 0 -> 3268 bytes .../GeometryCombineMode_Xor.expected.png | Bin 0 -> 4047 bytes .../GeometryCombineMode_Exclude.expected.png | Bin 0 -> 1691 bytes ...GeometryCombineMode_Intersect.expected.png | Bin 0 -> 1618 bytes .../GeometryCombineMode_Union.expected.png | Bin 0 -> 3268 bytes .../GeometryCombineMode_Xor.expected.png | Bin 0 -> 4047 bytes 20 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 src/Avalonia.Visuals/Media/CombinedGeometry.cs create mode 100644 src/Skia/Avalonia.Skia/CombinedGeometryImpl.cs create mode 100644 src/Windows/Avalonia.Direct2D1/Media/CombinedGeometryImpl.cs create mode 100644 tests/Avalonia.RenderTests/Media/CombinedGeometryTests.cs create mode 100644 tests/TestFiles/Direct2D1/Media/CombinedGeometry/GeometryCombineMode_Exclude.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/CombinedGeometry/GeometryCombineMode_Intersect.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/CombinedGeometry/GeometryCombineMode_Union.expected.png create mode 100644 tests/TestFiles/Direct2D1/Media/CombinedGeometry/GeometryCombineMode_Xor.expected.png create mode 100644 tests/TestFiles/Skia/Media/CombinedGeometry/GeometryCombineMode_Exclude.expected.png create mode 100644 tests/TestFiles/Skia/Media/CombinedGeometry/GeometryCombineMode_Intersect.expected.png create mode 100644 tests/TestFiles/Skia/Media/CombinedGeometry/GeometryCombineMode_Union.expected.png create mode 100644 tests/TestFiles/Skia/Media/CombinedGeometry/GeometryCombineMode_Xor.expected.png diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index abc07d0e71..63cbfb2dbe 100644 --- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -48,6 +48,7 @@ namespace Avalonia.Headless public IStreamGeometryImpl CreateStreamGeometry() => new HeadlessStreamingGeometryStub(); public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) => throw new NotImplementedException(); + public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2) => throw new NotImplementedException(); public IRenderTarget CreateRenderTarget(IEnumerable surfaces) => new HeadlessRenderTarget(); diff --git a/src/Avalonia.Visuals/Media/CombinedGeometry.cs b/src/Avalonia.Visuals/Media/CombinedGeometry.cs new file mode 100644 index 0000000000..2202030b7a --- /dev/null +++ b/src/Avalonia.Visuals/Media/CombinedGeometry.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Avalonia.Platform; + +#nullable enable + +namespace Avalonia.Media +{ + public enum GeometryCombineMode + { + /// + /// The two regions are combined by taking the union of both. The resulting geometry is + /// geometry A + geometry B. + /// + Union, + + /// + /// The two regions are combined by taking their intersection. The new area consists of the + /// overlapping region between the two geometries. + /// + Intersect, + + /// + /// The two regions are combined by taking the area that exists in the first region but not + /// the second and the area that exists in the second region but not the first. The new + /// region consists of (A-B) + (B-A), where A and B are geometries. + /// + Xor, + + /// + /// The second region is excluded from the first. Given two geometries, A and B, the area of + /// geometry B is removed from the area of geometry A, producing a region that is A-B. + /// + Exclude, + } + + /// + /// Represents a 2-D geometric shape defined by the combination of two Geometry objects. + /// + public class CombinedGeometry : Geometry + { + /// + /// Defines the property. + /// + public static readonly StyledProperty Geometry1Property = + AvaloniaProperty.Register(nameof(Geometry1)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty Geometry2Property = + AvaloniaProperty.Register(nameof(Geometry2)); + /// + /// Defines the property. + /// + public static readonly StyledProperty GeometryCombineModeProperty = + AvaloniaProperty.Register(nameof(GeometryCombineMode)); + + /// + /// Initializes a new instance of the class. + /// + public CombinedGeometry() + { + } + + /// + /// Initializes a new instance of the class with the + /// specified objects. + /// + /// The first geometry to combine. + /// The second geometry to combine. + public CombinedGeometry(Geometry geometry1, Geometry geometry2) + { + Geometry1 = geometry1; + Geometry2 = geometry2; + } + + /// + /// Initializes a new instance of the class with the + /// specified objects and . + /// + /// The method by which geometry1 and geometry2 are combined. + /// The first geometry to combine. + /// The second geometry to combine. + public CombinedGeometry(GeometryCombineMode combineMode, Geometry? geometry1, Geometry? geometry2) + { + Geometry1 = geometry1; + Geometry2 = geometry2; + GeometryCombineMode = combineMode; + } + + /// + /// Initializes a new instance of the class with the + /// specified objects, and + /// . + /// + /// The method by which geometry1 and geometry2 are combined. + /// The first geometry to combine. + /// The second geometry to combine. + /// The transform applied to the geometry. + public CombinedGeometry( + GeometryCombineMode combineMode, + Geometry? geometry1, + Geometry? geometry2, + Transform? transform) + { + Geometry1 = geometry1; + Geometry2 = geometry2; + GeometryCombineMode = combineMode; + Transform = transform; + } + + /// + /// Gets or sets the first object of this + /// object. + /// + public Geometry? Geometry1 + { + get => GetValue(Geometry1Property); + set => SetValue(Geometry1Property, value); + } + + /// + /// Gets or sets the second object of this + /// object. + /// + public Geometry? Geometry2 + { + get => GetValue(Geometry2Property); + set => SetValue(Geometry2Property, value); + } + + /// + /// Gets or sets the method by which the two geometries (specified by the + /// and properties) are combined. The + /// default value is . + /// + public GeometryCombineMode GeometryCombineMode + { + get => GetValue(GeometryCombineModeProperty); + set => SetValue(GeometryCombineModeProperty, value); + } + + public override Geometry Clone() + { + return new CombinedGeometry(GeometryCombineMode, Geometry1, Geometry2, Transform); + } + + protected override IGeometryImpl? CreateDefiningGeometry() + { + var g1 = Geometry1; + var g2 = Geometry2; + + if (g1 is object && g2 is object) + { + var factory = AvaloniaLocator.Current.GetService(); + return factory.CreateCombinedGeometry(GeometryCombineMode, g1, g2); + } + else if (GeometryCombineMode == GeometryCombineMode.Intersect) + return null; + else if (g1 is object) + return g1.PlatformImpl; + else if (g2 is object) + return g2.PlatformImpl; + else + return null; + } + } +} diff --git a/src/Avalonia.Visuals/Media/GeometryGroup.cs b/src/Avalonia.Visuals/Media/GeometryGroup.cs index a3ce6a9dcd..edbe63d4bb 100644 --- a/src/Avalonia.Visuals/Media/GeometryGroup.cs +++ b/src/Avalonia.Visuals/Media/GeometryGroup.cs @@ -50,7 +50,7 @@ namespace Avalonia.Media public override Geometry Clone() { - var result = new GeometryGroup { FillRule = FillRule }; + var result = new GeometryGroup { FillRule = FillRule, Transform = Transform }; if (_children?.Count > 0) result.Children = new GeometryCollection(_children); return result; diff --git a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs index 2cc44681d5..772f1ac9f3 100644 --- a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs @@ -67,6 +67,15 @@ namespace Avalonia.Platform /// A combined geometry. IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children); + /// + /// Creates a geometry group implementation. + /// + /// The combine mode + /// The first geometry. + /// The second geometry. + /// A combined geometry. + IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2); + /// /// Creates a renderer. /// diff --git a/src/Skia/Avalonia.Skia/CombinedGeometryImpl.cs b/src/Skia/Avalonia.Skia/CombinedGeometryImpl.cs new file mode 100644 index 0000000000..40d7e10ae3 --- /dev/null +++ b/src/Skia/Avalonia.Skia/CombinedGeometryImpl.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using Avalonia.Media; +using SkiaSharp; + +#nullable enable + +namespace Avalonia.Skia +{ + /// + /// A Skia implementation of a . + /// + internal class CombinedGeometryImpl : GeometryImpl + { + public CombinedGeometryImpl(GeometryCombineMode combineMode, Geometry g1, Geometry g2) + { + var path1 = ((GeometryImpl)g1.PlatformImpl).EffectivePath; + var path2 = ((GeometryImpl)g2.PlatformImpl).EffectivePath; + var op = combineMode switch + { + GeometryCombineMode.Intersect => SKPathOp.Intersect, + GeometryCombineMode.Xor => SKPathOp.Xor, + GeometryCombineMode.Exclude => SKPathOp.Difference, + _ => SKPathOp.Union, + }; + + var path = path1.Op(path2, op); + + EffectivePath = path; + Bounds = path.Bounds.ToAvaloniaRect(); + } + + public override Rect Bounds { get; } + public override SKPath EffectivePath { get; } + } +} diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 447d683a2c..e2175f1145 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -67,6 +67,11 @@ namespace Avalonia.Skia return new GeometryGroupImpl(fillRule, children); } + public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2) + { + return new CombinedGeometryImpl(combineMode, g1, g2); + } + /// public IBitmapImpl LoadBitmap(string fileName) { diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index 91f81223a0..eef4416101 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -176,6 +176,7 @@ namespace Avalonia.Direct2D1 public IGeometryImpl CreateRectangleGeometry(Rect rect) => new RectangleGeometryImpl(rect); public IStreamGeometryImpl CreateStreamGeometry() => new StreamGeometryImpl(); public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList children) => new GeometryGroupImpl(fillRule, children); + public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2) => new CombinedGeometryImpl(combineMode, g1, g2); /// public IBitmapImpl LoadBitmap(string fileName) diff --git a/src/Windows/Avalonia.Direct2D1/Media/CombinedGeometryImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/CombinedGeometryImpl.cs new file mode 100644 index 0000000000..5a13c10bbc --- /dev/null +++ b/src/Windows/Avalonia.Direct2D1/Media/CombinedGeometryImpl.cs @@ -0,0 +1,36 @@ +using SharpDX.Direct2D1; +using AM = Avalonia.Media; + +namespace Avalonia.Direct2D1.Media +{ + /// + /// A Direct2D implementation of a . + /// + internal class CombinedGeometryImpl : GeometryImpl + { + /// + /// Initializes a new instance of the class. + /// + public CombinedGeometryImpl( + AM.GeometryCombineMode combineMode, + AM.Geometry geometry1, + AM.Geometry geometry2) + : base(CreateGeometry(combineMode, geometry1, geometry2)) + { + } + + private static Geometry CreateGeometry( + AM.GeometryCombineMode combineMode, + AM.Geometry geometry1, + AM.Geometry geometry2) + { + var g1 = ((GeometryImpl)geometry1.PlatformImpl).Geometry; + var g2 = ((GeometryImpl)geometry2.PlatformImpl).Geometry; + var dest = new PathGeometry(Direct2D1Platform.Direct2D1Factory); + using var sink = dest.Open(); + g1.Combine(g2, (CombineMode)combineMode, sink); + sink.Close(); + return dest; + } + } +} diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs index fd8dd3ff94..3e11c74e1c 100644 --- a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs +++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs @@ -41,6 +41,11 @@ namespace Avalonia.Benchmarks throw new NotImplementedException(); } + public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2) + { + throw new NotImplementedException(); + } + public IRenderTarget CreateRenderTarget(IEnumerable surfaces) { throw new NotImplementedException(); diff --git a/tests/Avalonia.RenderTests/Media/CombinedGeometryTests.cs b/tests/Avalonia.RenderTests/Media/CombinedGeometryTests.cs new file mode 100644 index 0000000000..0469dec651 --- /dev/null +++ b/tests/Avalonia.RenderTests/Media/CombinedGeometryTests.cs @@ -0,0 +1,56 @@ +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Controls.Shapes; +using Avalonia.Media; +using Xunit; + +#if AVALONIA_SKIA +namespace Avalonia.Skia.RenderTests +#else +namespace Avalonia.Direct2D1.RenderTests.Media +#endif +{ + public class CombinedGeometryTests : TestBase + { + public CombinedGeometryTests() + : base(@"Media\CombinedGeometry") + { + } + + [Theory] + [InlineData(Avalonia.Media.GeometryCombineMode.Union)] + [InlineData(Avalonia.Media.GeometryCombineMode.Intersect)] + [InlineData(Avalonia.Media.GeometryCombineMode.Xor)] + [InlineData(Avalonia.Media.GeometryCombineMode.Exclude)] + public async Task GeometryCombineMode(GeometryCombineMode mode) + { + var target = new Border + { + Width = 200, + Height = 200, + Background = Brushes.White, + Child = new Path + { + Data = new CombinedGeometry + { + GeometryCombineMode = mode, + Geometry1 = new RectangleGeometry(new Rect(25, 25, 100, 100)), + Geometry2 = new EllipseGeometry + { + Center = new Point(125, 125), + RadiusX = 50, + RadiusY = 50, + } + }, + Fill = Brushes.Blue, + Stroke = Brushes.Red, + StrokeThickness = 1, + } + }; + + var testName = $"GeometryCombineMode_{mode}"; + await RenderToFile(target, testName); + CompareImages(testName); + } + } +} diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs index 34697b8616..1f632034be 100644 --- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs +++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs @@ -57,6 +57,11 @@ namespace Avalonia.UnitTests return Mock.Of(); } + public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2) + { + return Mock.Of(); + } + public IWriteableBitmapImpl CreateWriteableBitmap( PixelSize size, Vector dpi, diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs index dded3caa88..229bb8aef3 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs +++ b/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs @@ -42,6 +42,11 @@ namespace Avalonia.Visuals.UnitTests.VisualTree throw new NotImplementedException(); } + public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2) + { + throw new NotImplementedException(); + } + public IBitmapImpl LoadBitmap(Stream stream) { throw new NotImplementedException(); diff --git a/tests/TestFiles/Direct2D1/Media/CombinedGeometry/GeometryCombineMode_Exclude.expected.png b/tests/TestFiles/Direct2D1/Media/CombinedGeometry/GeometryCombineMode_Exclude.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..2c4aa99eeb4dab763fc8ebb91dc2b9d47c4e6cfc GIT binary patch literal 1691 zcmc&#>pK$)7#~&DHOr7C?f7hXVjm!&F=k+5rIUbar)e@V*`}JL-}07eTj8 z8W=M;)9v)@_%GLkO$6i? zde<-hE$Vqg(=BCVnw$ufY)(PdTsineU>rD%&><%g60;MLjt~||{xDEhf&O!-6>o{} zMGn@7gG9WVmAP-1q5H%H-wf0H#}+5YvUAf`SEo7O3vFVAO`kzmBJ9ACk(&k_Zr$Ez z0q5_`kHiW(C`X-s!^%?YmSi$&#T@xIb z8IMg{BiC&%;CZR)s<7Arnsb#xu_)6*?-JEGVH~;_{A~G}<*JA);MwTma?VwM1oj%1 zPH?65spkCoE>zUW-<&i-nvd4w^0?f$!DkeTRj3x-g7Nc_8I&st`VVMSGeIjpsU`%y zVUp_#$t|NE_km+KLI+2s!)OWfi%8@&GwO$MLx9?k7>#2$N*7yClHPHPY8H}HiqK-h zS3}t$;I7G7spDjx^xkA%c6_K$&t%gN#ji|NR#k1dYkSCC~G1HxW!pTn~`|fi~YL_Wj<%@-Y7fEq?6%U0MG3D>NY(AZ-yAF5)QnUQSx zlUZ2J>|=x#Ny&n~fE*G@F>7<-n-dDr3>ib#wX;hs#JiQNb>R56MjsRsA}-VMTYNHb zIjZY*$WpYD0==S$bn6`*1q&8W4aF-aVj<#Uo!SI(LsB+0#0%~YtljhGHCCk&>oWO* zIQFsaRpCL_H49i2v|*f_h#E*{Avs>Q$?N%*DlYRQ{a;`7sJ8n+`%mhgDX^b&Z__tm zu{~Xwe!~3p8VX_BROtX6pVTC{AuZ1#E?lK26&kRtjVYZ7Z3H7VM}2YM?Yqc`10}udHcZtT%EB_9LEc{{sp+}&;kGe literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Media/CombinedGeometry/GeometryCombineMode_Intersect.expected.png b/tests/TestFiles/Direct2D1/Media/CombinedGeometry/GeometryCombineMode_Intersect.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..abb3cea27080e77aa6063148ee9a5814ff9ea99d GIT binary patch literal 1618 zcmdT_`!~}I0H3wVBaO<<~5kL~U%8Om;CVYqRx zlvij*m|kWSp*AAaRz{;)!}XZC?w@gg_`H7k{Pa2J!#EWdq^D!70|J5cLW2Dxwp8#R zv^2N44qIQgB^s27AYV}ZfXVDufF$^w^Z|hy^K@0`pdb)b72@ynbMi0q6ERu1B-3XK z{GCkRlfj#RLc0TdGg!$Qhd9B#1-20Z7ohivH5`3nA~@aPVW4xsI)XDXn6uXk#=Fq% z>r&T!T6j+z}o*`%g~c*K8m+K*$fB7{{Cn-&B4_3Cv;6I?J62v z1gXRLR~mFqsV$y%(GIxqhI4DeT_o?P?EzKarN+nlK-xPI7MGh$kYnF3f6uBOsd3FW z5V#S!mOIUFFH?eI6>V*!sxx-f^Z&uQU4^#kvf@pdjwUqIH1 z!xiB3HB%WQ7Fm``bi2Az`tFXSuxjI18qdo$6>-#(EjFi-n^R zomm`fCOR?`SZ7rz%l3(f$~zBCHZ73n9L^W=4RKU3+;D@s2bN~}RNiU9TUW}&{hcgA zIGW4FCV=5k3T5cpc)Mh^M{Oei#xt7oipit(91#YJ@=>Bl^uhE}kJZ^!$!(gKiMiY? zi_pj!6YSR)q$F}Gl=n--H!>RdEtBQ3E|=?EkjEiSX4BG2?nZ{<3!Vs^*n9?s%{UrE z$Zh=tf$RJj@%It4+YM!_m_0xMhmN*lzOEjP%%f4MNgqem@m_}9C=xs#+-5R1KQp!b zVMQVdDy05RUQA8XimTHT&rrV*fKP*`fsI+ep=?r(UhXz}ZKJx>Q}Su5rAO*<%xTid z4;=0H^`4~REmy2tDvu%=xq_qZnwx=?^R%=di{bO`{CgoPBGPwHW%I){0&k>F@(cLuCm_d$zid^%YBOT>{9V7k%#)zOI} zA~fG0!K&s;?iv8lb@Um>1oYY5#p|F& zVH)&WlsokKKOXp6HQ|nS9Cm(rSY z;%-%?G%^t3*SZ$>&MdA^E3U1$-;HxEr^G1~PCq!p*bS(Tr{*PEGQZkpW3knRMaJOh zdNQo3_ZGbB)>c6))*gApg}tx3u=l-eD0v~+gkdVoRy-jrXcJSnm!Dg>*6Kz-7*z?V z9kq!ZCCuSCf{E0mUq>M<^n CHK(Eg literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Media/CombinedGeometry/GeometryCombineMode_Union.expected.png b/tests/TestFiles/Direct2D1/Media/CombinedGeometry/GeometryCombineMode_Union.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..f431ef4403d76b9798ea4d048d80caf909856bbd GIT binary patch literal 3268 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 GCHg-yqwvE3 literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Direct2D1/Media/CombinedGeometry/GeometryCombineMode_Xor.expected.png b/tests/TestFiles/Direct2D1/Media/CombinedGeometry/GeometryCombineMode_Xor.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..4dd547c4bf5b01343a211277795b2201e639c16b GIT binary patch literal 4047 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 literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/CombinedGeometry/GeometryCombineMode_Intersect.expected.png b/tests/TestFiles/Skia/Media/CombinedGeometry/GeometryCombineMode_Intersect.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..abb3cea27080e77aa6063148ee9a5814ff9ea99d GIT binary patch literal 1618 zcmdT_`!~}I0H3wVBaO<<~5kL~U%8Om;CVYqRx zlvij*m|kWSp*AAaRz{;)!}XZC?w@gg_`H7k{Pa2J!#EWdq^D!70|J5cLW2Dxwp8#R zv^2N44qIQgB^s27AYV}ZfXVDufF$^w^Z|hy^K@0`pdb)b72@ynbMi0q6ERu1B-3XK z{GCkRlfj#RLc0TdGg!$Qhd9B#1-20Z7ohivH5`3nA~@aPVW4xsI)XDXn6uXk#=Fq% z>r&T!T6j+z}o*`%g~c*K8m+K*$fB7{{Cn-&B4_3Cv;6I?J62v z1gXRLR~mFqsV$y%(GIxqhI4DeT_o?P?EzKarN+nlK-xPI7MGh$kYnF3f6uBOsd3FW z5V#S!mOIUFFH?eI6>V*!sxx-f^Z&uQU4^#kvf@pdjwUqIH1 z!xiB3HB%WQ7Fm``bi2Az`tFXSuxjI18qdo$6>-#(EjFi-n^R zomm`fCOR?`SZ7rz%l3(f$~zBCHZ73n9L^W=4RKU3+;D@s2bN~}RNiU9TUW}&{hcgA zIGW4FCV=5k3T5cpc)Mh^M{Oei#xt7oipit(91#YJ@=>Bl^uhE}kJZ^!$!(gKiMiY? zi_pj!6YSR)q$F}Gl=n--H!>RdEtBQ3E|=?EkjEiSX4BG2?nZ{<3!Vs^*n9?s%{UrE z$Zh=tf$RJj@%It4+YM!_m_0xMhmN*lzOEjP%%f4MNgqem@m_}9C=xs#+-5R1KQp!b zVMQVdDy05RUQA8XimTHT&rrV*fKP*`fsI+ep=?r(UhXz}ZKJx>Q}Su5rAO*<%xTid z4;=0H^`4~REmy2tDvu%=xq_qZnwx=?^R%=di{bO`{CgoPBGPwHW%I){0&k>F@(cLuCm_d$zid^%YBOT>{9V7k%#)zOI} zA~fG0!K&s;?iv8lb@Um>1oYY5#p|F& zVH)&WlsokKKOXp6HQ|nS9Cm(rSY z;%-%?G%^t3*SZ$>&MdA^E3U1$-;HxEr^G1~PCq!p*bS(Tr{*PEGQZkpW3knRMaJOh zdNQo3_ZGbB)>c6))*gApg}tx3u=l-eD0v~+gkdVoRy-jrXcJSnm!Dg>*6Kz-7*z?V z9kq!ZCCuSCf{E0mUq>M<^n CHK(Eg literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/CombinedGeometry/GeometryCombineMode_Union.expected.png b/tests/TestFiles/Skia/Media/CombinedGeometry/GeometryCombineMode_Union.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..f431ef4403d76b9798ea4d048d80caf909856bbd GIT binary patch literal 3268 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 GCHg-yqwvE3 literal 0 HcmV?d00001 diff --git a/tests/TestFiles/Skia/Media/CombinedGeometry/GeometryCombineMode_Xor.expected.png b/tests/TestFiles/Skia/Media/CombinedGeometry/GeometryCombineMode_Xor.expected.png new file mode 100644 index 0000000000000000000000000000000000000000..4dd547c4bf5b01343a211277795b2201e639c16b GIT binary patch literal 4047 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 W