From f0b9f96429ee380e65c19f80d1e99c94b36248f3 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Sun, 14 Jan 2024 13:41:53 +0100 Subject: [PATCH 01/31] Initial implementation --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 Pinta.Effects/Effects/VoronoiDiagramEffect.cs diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs new file mode 100644 index 000000000..7912f8e20 --- /dev/null +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.ComponentModel; +using System.Linq; +using Cairo; +using Pinta.Core; +using Pinta.Gui.Widgets; + +namespace Pinta.Effects; + +public sealed class VoronoiDiagramEffect : BaseEffect +{ + // TODO: Icon + + public override bool IsTileable => false; + + public override string Name => Translations.GetString ("Voronoi Diagram"); + + public override bool IsConfigurable => true; + + public override string EffectMenuCategory => Translations.GetString ("Render"); + + public VoronoiDiagramData Data => (VoronoiDiagramData) EffectData!; // NRT - Set in constructor + + public VoronoiDiagramEffect () + { + EffectData = new VoronoiDiagramData (); + } + + public override void LaunchConfiguration () + { + EffectHelper.LaunchSimpleEffectDialog (this); + } + + protected override void Render (ImageSurface src, ImageSurface dst, RectangleI roi) + { + int w = dst.Width; + int h = dst.Height; + + ImmutableArray points = + CreatePoints (roi, Data.PointCount, Data.PointLocationsSeed) + .OrderBy (p => p.X) + .ToImmutableArray (); + + ImmutableArray colors = CreateColors (points.Length, Data.ColorsSeed).ToImmutableArray (); + + Func distanceCalculator = GetDistanceCalculator (Data.DistanceCalculationMethod); + + Span dst_data = dst.GetPixelData (); + + for (int y = roi.Top; y <= roi.Bottom; y++) { + var dst_row = dst_data.Slice (y * w, w); + for (int x = roi.Left; x <= roi.Right; x++) { + PointI pixelLocation = new (x, y); + double shortestDistance = double.MaxValue; + int closestIndex = 0; + for (var i = 0; i < points.Length; i++) { + var point = points[i]; + double distance = distanceCalculator (point, pixelLocation); + if (distance > shortestDistance) continue; + closestIndex = i; + } + dst_row[x] = colors[closestIndex]; + } + } + } + + private static Func GetDistanceCalculator (DistanceCalculationMethod distanceCalculationMethod) + { + return distanceCalculationMethod switch { + DistanceCalculationMethod.Euclidean => Euclidean, + DistanceCalculationMethod.Manhattan => Manhattan, + _ => throw new InvalidEnumArgumentException (nameof (distanceCalculationMethod), (int) distanceCalculationMethod, typeof (DistanceCalculationMethod)), + }; + + static double Euclidean (PointI targetPoint, PointI pixelLocation) + { + PointI difference = pixelLocation - targetPoint; + return difference.Magnitude (); + } + + static double Manhattan (PointI targetPoint, PointI pixelLocation) + { + PointI difference = pixelLocation - targetPoint; + return Math.Abs (difference.X) + Math.Abs (difference.Y); + } + } + + private static HashSet CreatePoints (RectangleI roi, int pointCount, RandomSeed pointLocationsSeed) + { + int effectivePointCount = Math.Min (pointCount, roi.Width * roi.Height); + + Random randomPositioner = new (pointLocationsSeed.Value); + HashSet result = new (); // Ensures points' uniqueness + + while (result.Count < effectivePointCount) { + + PointI point = new ( + X: randomPositioner.Next (roi.Left, roi.Right + 1), + Y: randomPositioner.Next (roi.Top, roi.Bottom + 1) + ); + + result.Add (point); + } + + return result; + } + + private static HashSet CreateColors (int colorCount, RandomSeed colorsSeed) + { + Random randomColorizer = new (colorsSeed.Value); + HashSet result = new (); // Ensures points' uniqueness + + while (result.Count < colorCount) + result.Add (randomColorizer.RandomColorBgra ()); + + return result; + } + + public sealed class VoronoiDiagramData : EffectData + { + [Caption ("Distance Calculation Method")] + public DistanceCalculationMethod DistanceCalculationMethod { get; set; } = DistanceCalculationMethod.Euclidean; + + [Caption ("Point Count"), MinimumValue (1), MaximumValue (65_535)] + public int PointCount { get; set; } = 100; + + [Caption ("Colors Seed")] + public RandomSeed ColorsSeed { get; set; } = new (0); + + [Caption ("Point Locations Seed")] + public RandomSeed PointLocationsSeed { get; set; } = new (0); + } + + public enum DistanceCalculationMethod + { + Euclidean, + Manhattan, + } +} From 543f89abfdc9c3854de761d89791d1f6a3fb9dd5 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Sun, 14 Jan 2024 13:44:56 +0100 Subject: [PATCH 02/31] Added it to `CoreEffectsExtension` --- Pinta.Effects/CoreEffectsExtension.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Pinta.Effects/CoreEffectsExtension.cs b/Pinta.Effects/CoreEffectsExtension.cs index 21efe9533..e59ad43cd 100644 --- a/Pinta.Effects/CoreEffectsExtension.cs +++ b/Pinta.Effects/CoreEffectsExtension.cs @@ -83,6 +83,7 @@ public void Initialize () PintaCore.Effects.RegisterEffect (new TileEffect ()); PintaCore.Effects.RegisterEffect (new TwistEffect ()); PintaCore.Effects.RegisterEffect (new UnfocusEffect ()); + PintaCore.Effects.RegisterEffect (new VoronoiDiagramEffect ()); PintaCore.Effects.RegisterEffect (new ZoomBlurEffect ()); } @@ -128,6 +129,7 @@ public void Uninitialize () PintaCore.Effects.UnregisterInstanceOfEffect (typeof (TileEffect)); PintaCore.Effects.UnregisterInstanceOfEffect (typeof (TwistEffect)); PintaCore.Effects.UnregisterInstanceOfEffect (typeof (UnfocusEffect)); + PintaCore.Effects.UnregisterInstanceOfEffect (typeof (VoronoiDiagramEffect)); PintaCore.Effects.UnregisterInstanceOfEffect (typeof (ZoomBlurEffect)); } #endregion From 08dbde2f63fc2e9d87d6157c9b75254d1295c081 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Sun, 14 Jan 2024 13:49:16 +0100 Subject: [PATCH 03/31] Assigned distance --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index 7912f8e20..c021dec7d 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -59,6 +59,7 @@ protected override void Render (ImageSurface src, ImageSurface dst, RectangleI r var point = points[i]; double distance = distanceCalculator (point, pixelLocation); if (distance > shortestDistance) continue; + shortestDistance = distance; closestIndex = i; } dst_row[x] = colors[closestIndex]; From c9da51b88f2f8de5db65778785a5a85d51d0e57d Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Sun, 14 Jan 2024 14:11:01 +0100 Subject: [PATCH 04/31] Reduced number of points --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index c021dec7d..7c04a8629 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -124,7 +124,7 @@ public sealed class VoronoiDiagramData : EffectData [Caption ("Distance Calculation Method")] public DistanceCalculationMethod DistanceCalculationMethod { get; set; } = DistanceCalculationMethod.Euclidean; - [Caption ("Point Count"), MinimumValue (1), MaximumValue (65_535)] + [Caption ("Point Count"), MinimumValue (1), MaximumValue (1024)] public int PointCount { get; set; } = 100; [Caption ("Colors Seed")] From 4d3f34c401ca5f5d9de62cd317e9cc14e53c6b16 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Sun, 14 Jan 2024 17:56:46 +0100 Subject: [PATCH 05/31] Using immutable structures --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index 7c04a8629..d8495d6a0 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -88,12 +88,12 @@ static double Manhattan (PointI targetPoint, PointI pixelLocation) } } - private static HashSet CreatePoints (RectangleI roi, int pointCount, RandomSeed pointLocationsSeed) + private static ImmutableHashSet CreatePoints (RectangleI roi, int pointCount, RandomSeed pointLocationsSeed) { int effectivePointCount = Math.Min (pointCount, roi.Width * roi.Height); Random randomPositioner = new (pointLocationsSeed.Value); - HashSet result = new (); // Ensures points' uniqueness + var result = ImmutableHashSet.CreateBuilder (); // Ensures points' uniqueness while (result.Count < effectivePointCount) { @@ -105,18 +105,18 @@ private static HashSet CreatePoints (RectangleI roi, int pointCount, Ran result.Add (point); } - return result; + return result.ToImmutable (); } - private static HashSet CreateColors (int colorCount, RandomSeed colorsSeed) + private static ImmutableHashSet CreateColors (int colorCount, RandomSeed colorsSeed) { Random randomColorizer = new (colorsSeed.Value); - HashSet result = new (); // Ensures points' uniqueness + var result = ImmutableHashSet.CreateBuilder (); // Ensures points' uniqueness while (result.Count < colorCount) result.Add (randomColorizer.RandomColorBgra ()); - return result; + return result.ToImmutable (); } public sealed class VoronoiDiagramData : EffectData From e1467632a8b57c59a544e487c06def7eb93f2e1a Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Sun, 14 Jan 2024 18:02:20 +0100 Subject: [PATCH 06/31] Added Chebyshev method of distance calculation --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index d8495d6a0..21370711f 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -72,6 +72,7 @@ private static Func GetDistanceCalculator (DistanceCalcu return distanceCalculationMethod switch { DistanceCalculationMethod.Euclidean => Euclidean, DistanceCalculationMethod.Manhattan => Manhattan, + DistanceCalculationMethod.Chebyshev => Chebyshev, _ => throw new InvalidEnumArgumentException (nameof (distanceCalculationMethod), (int) distanceCalculationMethod, typeof (DistanceCalculationMethod)), }; @@ -86,6 +87,12 @@ static double Manhattan (PointI targetPoint, PointI pixelLocation) PointI difference = pixelLocation - targetPoint; return Math.Abs (difference.X) + Math.Abs (difference.Y); } + + static double Chebyshev (PointI targetPoint, PointI pixelLocation) + { + PointI difference = pixelLocation - targetPoint; + return Math.Max (Math.Abs (difference.X), Math.Abs (difference.Y)); + } } private static ImmutableHashSet CreatePoints (RectangleI roi, int pointCount, RandomSeed pointLocationsSeed) @@ -138,5 +145,6 @@ public enum DistanceCalculationMethod { Euclidean, Manhattan, + Chebyshev, } } From 4cd5095810684766dfcbeaeb7f3884fd2dad29af Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Sun, 14 Jan 2024 18:44:54 +0100 Subject: [PATCH 07/31] Added color sorting --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 113 ++++++++++++++++-- 1 file changed, 103 insertions(+), 10 deletions(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index 21370711f..b7e8d5441 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -38,12 +38,26 @@ protected override void Render (ImageSurface src, ImageSurface dst, RectangleI r int w = dst.Width; int h = dst.Height; + ColorSorting colorSorting = Data.ColorSorting; + ImmutableArray points = - CreatePoints (roi, Data.PointCount, Data.PointLocationsSeed) - .OrderBy (p => p.X) + SortPoints ( + CreatePoints ( + roi, + Data.PointCount, + Data.PointLocationsSeed), + colorSorting + ) .ToImmutableArray (); - ImmutableArray colors = CreateColors (points.Length, Data.ColorsSeed).ToImmutableArray (); + ImmutableArray colors = + SortColors ( + CreateColors ( + points.Length, + Data.ColorsSeed), + colorSorting + ) + .ToImmutableArray (); Func distanceCalculator = GetDistanceCalculator (Data.DistanceCalculationMethod); @@ -67,6 +81,62 @@ protected override void Render (ImageSurface src, ImageSurface dst, RectangleI r } } + private static IEnumerable SortColors (IEnumerable baseColors, ColorSorting colorSorting) + { + switch (colorSorting) { + case ColorSorting.Random: + return baseColors; + case ColorSorting.HorizontalBGR: + case ColorSorting.VerticalBGR: + return baseColors.OrderBy (p => p.B).ThenBy (p => p.G).ThenBy (p => p.R); + case ColorSorting.HorizontalBRG: + case ColorSorting.VerticalBRG: + return baseColors.OrderBy (p => p.B).ThenBy (p => p.R).ThenBy (p => p.G); + case ColorSorting.HorizontalGBR: + case ColorSorting.VerticalGBR: + return baseColors.OrderBy (p => p.G).ThenBy (p => p.B).ThenBy (p => p.R); + case ColorSorting.HorizontalGRB: + case ColorSorting.VerticalGRB: + return baseColors.OrderBy (p => p.G).ThenBy (p => p.R).ThenBy (p => p.B); + case ColorSorting.HorizontalRBG: + case ColorSorting.VerticalRBG: + return baseColors.OrderBy (p => p.R).ThenBy (p => p.B).ThenBy (p => p.G); + case ColorSorting.HorizontalRGB: + case ColorSorting.VerticalRGB: + return baseColors.OrderBy (p => p.R).ThenBy (p => p.G).ThenBy (p => p.B); + default: + throw new InvalidEnumArgumentException (nameof (baseColors), (int) colorSorting, typeof (ColorSorting)); + } + } + + private static IEnumerable SortPoints (IEnumerable basePoints, ColorSorting colorSorting) + { + switch (colorSorting) { + + case ColorSorting.Random: + return basePoints; + + case ColorSorting.HorizontalBGR: + case ColorSorting.HorizontalBRG: + case ColorSorting.HorizontalGBR: + case ColorSorting.HorizontalGRB: + case ColorSorting.HorizontalRBG: + case ColorSorting.HorizontalRGB: + return basePoints.OrderBy (p => p.X); + + case ColorSorting.VerticalBGR: + case ColorSorting.VerticalBRG: + case ColorSorting.VerticalGBR: + case ColorSorting.VerticalGRB: + case ColorSorting.VerticalRBG: + case ColorSorting.VerticalRGB: + return basePoints.OrderBy (p => p.Y); + + default: + throw new InvalidEnumArgumentException (nameof (colorSorting), (int) colorSorting, typeof (ColorSorting)); + } + } + private static Func GetDistanceCalculator (DistanceCalculationMethod distanceCalculationMethod) { return distanceCalculationMethod switch { @@ -115,15 +185,16 @@ private static ImmutableHashSet CreatePoints (RectangleI roi, int pointC return result.ToImmutable (); } - private static ImmutableHashSet CreateColors (int colorCount, RandomSeed colorsSeed) + private static IEnumerable CreateColors (int colorCount, RandomSeed colorsSeed) { Random randomColorizer = new (colorsSeed.Value); - var result = ImmutableHashSet.CreateBuilder (); // Ensures points' uniqueness - - while (result.Count < colorCount) - result.Add (randomColorizer.RandomColorBgra ()); - - return result.ToImmutable (); + HashSet uniquenessTracker = new (); + while (uniquenessTracker.Count < colorCount) { + ColorBgra candidateColor = randomColorizer.RandomColorBgra (); + if (uniquenessTracker.Contains (candidateColor)) continue; + uniquenessTracker.Add (candidateColor); + yield return candidateColor; + } } public sealed class VoronoiDiagramData : EffectData @@ -134,6 +205,9 @@ public sealed class VoronoiDiagramData : EffectData [Caption ("Point Count"), MinimumValue (1), MaximumValue (1024)] public int PointCount { get; set; } = 100; + [Caption ("Color Sorting")] + public ColorSorting ColorSorting { get; set; } = ColorSorting.Random; + [Caption ("Colors Seed")] public RandomSeed ColorsSeed { get; set; } = new (0); @@ -147,4 +221,23 @@ public enum DistanceCalculationMethod Manhattan, Chebyshev, } + + public enum ColorSorting + { + Random, + + HorizontalBGR, + HorizontalBRG, + HorizontalGBR, + HorizontalGRB, + HorizontalRBG, + HorizontalRGB, + + VerticalBGR, + VerticalBRG, + VerticalGBR, + VerticalGRB, + VerticalRBG, + VerticalRGB, + } } From 83c913672609de1949a5be9dc3ebbd419931be8c Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Sun, 14 Jan 2024 18:50:07 +0100 Subject: [PATCH 08/31] Added labels to sorting options --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index b7e8d5441..7c4f2f3b7 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -224,20 +224,20 @@ public enum DistanceCalculationMethod public enum ColorSorting { - Random, - - HorizontalBGR, - HorizontalBRG, - HorizontalGBR, - HorizontalGRB, - HorizontalRBG, - HorizontalRGB, - - VerticalBGR, - VerticalBRG, - VerticalGBR, - VerticalGRB, - VerticalRBG, - VerticalRGB, + [Caption ("Random")] Random, + + [Caption ("Horizontal (B, G, R)")] HorizontalBGR, + [Caption ("Horizontal (B, R, G)")] HorizontalBRG, + [Caption ("Horizontal (G, B, R)")] HorizontalGBR, + [Caption ("Horizontal (G, R, B)")] HorizontalGRB, + [Caption ("Horizontal (R, B, G)")] HorizontalRBG, + [Caption ("Horizontal (R, G, B)")] HorizontalRGB, + + [Caption ("Vertical (B, G, R)")] VerticalBGR, + [Caption ("Vertical (B, R, G)")] VerticalBRG, + [Caption ("Vertical (G, B, R)")] VerticalGBR, + [Caption ("Vertical (G, R, B)")] VerticalGRB, + [Caption ("Vertical (R, B, G)")] VerticalRBG, + [Caption ("Vertical (R, G, B)")] VerticalRGB, } } From c33fd4d5c89e355d8cdeeaa3216e73e6cfc583a1 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Sun, 14 Jan 2024 19:10:04 +0100 Subject: [PATCH 09/31] Added notes for translators, and added option to invert sorting --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 85 ++++++++++++++----- 1 file changed, 62 insertions(+), 23 deletions(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index 7c4f2f3b7..51cd530c5 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -50,14 +50,10 @@ protected override void Render (ImageSurface src, ImageSurface dst, RectangleI r ) .ToImmutableArray (); - ImmutableArray colors = - SortColors ( - CreateColors ( - points.Length, - Data.ColorsSeed), - colorSorting - ) - .ToImmutableArray (); + IEnumerable baseColors = CreateColors (points.Length, Data.ColorsSeed); + IEnumerable positionSortedColors = SortColors (baseColors, colorSorting); + IEnumerable reversedSortingColors = Data.ReverseColorSorting ? positionSortedColors.Reverse () : positionSortedColors; + ImmutableArray colors = reversedSortingColors.ToImmutableArray (); Func distanceCalculator = GetDistanceCalculator (Data.DistanceCalculationMethod); @@ -208,6 +204,10 @@ public sealed class VoronoiDiagramData : EffectData [Caption ("Color Sorting")] public ColorSorting ColorSorting { get; set; } = ColorSorting.Random; + // Translators: In this context, "reverse" is a verb, and the user can choose whether or not they want to reverse the color sorting + [Caption ("Reverse Color Sorting")] + public bool ReverseColorSorting { get; set; } = false; + [Caption ("Colors Seed")] public RandomSeed ColorsSeed { get; set; } = new (0); @@ -224,20 +224,59 @@ public enum DistanceCalculationMethod public enum ColorSorting { - [Caption ("Random")] Random, - - [Caption ("Horizontal (B, G, R)")] HorizontalBGR, - [Caption ("Horizontal (B, R, G)")] HorizontalBRG, - [Caption ("Horizontal (G, B, R)")] HorizontalGBR, - [Caption ("Horizontal (G, R, B)")] HorizontalGRB, - [Caption ("Horizontal (R, B, G)")] HorizontalRBG, - [Caption ("Horizontal (R, G, B)")] HorizontalRGB, - - [Caption ("Vertical (B, G, R)")] VerticalBGR, - [Caption ("Vertical (B, R, G)")] VerticalBRG, - [Caption ("Vertical (G, B, R)")] VerticalGBR, - [Caption ("Vertical (G, R, B)")] VerticalGRB, - [Caption ("Vertical (R, B, G)")] VerticalRBG, - [Caption ("Vertical (R, G, B)")] VerticalRGB, + // Translators: + [Caption ("Random Color Sorting")] Random, + + + + // Translators: Horizontal color sorting with B, then G as the leading terms + [Caption ("Horizontal (B, G, R)")] + HorizontalBGR, + + // Translators: Horizontal color sorting with B, then R as the leading terms + [Caption ("Horizontal (B, R, G)")] + HorizontalBRG, + + // Translators: Horizontal color sorting with G, then B as the leading terms + [Caption ("Horizontal (G, B, R)")] + HorizontalGBR, + + // Translators: Horizontal color sorting with G, then R as the leading terms + [Caption ("Horizontal (G, R, B)")] + HorizontalGRB, + + // Translators: Horizontal color sorting with R, then B as the leading terms + [Caption ("Horizontal (R, B, G)")] + HorizontalRBG, + + // Translators: Horizontal color sorting with R, then G as the leading terms + [Caption ("Horizontal (R, G, B)")] + HorizontalRGB, + + + + // Translators: Vertical color sorting with B, then G as the leading terms + [Caption ("Vertical (B, G, R)")] + VerticalBGR, + + // Translators: Vertical color sorting with B, then R as the leading terms + [Caption ("Vertical (B, R, G)")] + VerticalBRG, + + // Translators: Vertical color sorting with G, then B as the leading terms + [Caption ("Vertical (G, B, R)")] + VerticalGBR, + + // Translators: Vertical color sorting with G, then R as the leading terms + [Caption ("Vertical (G, R, B)")] + VerticalGRB, + + // Translators: Vertical color sorting with R, then B as the leading terms + [Caption ("Vertical (R, B, G)")] + VerticalRBG, + + // Translators: Vertical color sorting with R, then G as the leading terms + [Caption ("Vertical (R, G, B)")] + VerticalRGB, } } From e00033010d8128efd7d8c66281cef0dcf474140d Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Sun, 14 Jan 2024 19:13:23 +0100 Subject: [PATCH 10/31] Removed empty comment for translators --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index 51cd530c5..97cec53e1 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -224,7 +224,6 @@ public enum DistanceCalculationMethod public enum ColorSorting { - // Translators: [Caption ("Random Color Sorting")] Random, From b7d6b1412462c12c5a1e50b662cdfc2a190b9a93 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Sun, 14 Jan 2024 19:16:12 +0100 Subject: [PATCH 11/31] stored values in more variables --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index 97cec53e1..7ab640114 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -40,15 +40,8 @@ protected override void Render (ImageSurface src, ImageSurface dst, RectangleI r ColorSorting colorSorting = Data.ColorSorting; - ImmutableArray points = - SortPoints ( - CreatePoints ( - roi, - Data.PointCount, - Data.PointLocationsSeed), - colorSorting - ) - .ToImmutableArray (); + IEnumerable basePoints = CreatePoints (roi, Data.PointCount, Data.PointLocationsSeed); + ImmutableArray points = SortPoints (basePoints, colorSorting).ToImmutableArray (); IEnumerable baseColors = CreateColors (points.Length, Data.ColorsSeed); IEnumerable positionSortedColors = SortColors (baseColors, colorSorting); From 368f2cba8201879cee84bf66703ca5f0321f73c5 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Sun, 14 Jan 2024 19:24:23 +0100 Subject: [PATCH 12/31] Created new `record` type for settings `VoronoiSettings` --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index 7ab640114..c11cd2bb5 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -33,11 +33,15 @@ public override void LaunchConfiguration () EffectHelper.LaunchSimpleEffectDialog (this); } - protected override void Render (ImageSurface src, ImageSurface dst, RectangleI roi) + private sealed record VoronoiSettings ( + int w, + int h, + ImmutableArray points, + ImmutableArray colors, + Func distanceCalculator); + + private VoronoiSettings CreateSettings (ImageSurface dst, RectangleI roi) { - int w = dst.Width; - int h = dst.Height; - ColorSorting colorSorting = Data.ColorSorting; IEnumerable basePoints = CreatePoints (roi, Data.PointCount, Data.PointLocationsSeed); @@ -46,26 +50,36 @@ protected override void Render (ImageSurface src, ImageSurface dst, RectangleI r IEnumerable baseColors = CreateColors (points.Length, Data.ColorsSeed); IEnumerable positionSortedColors = SortColors (baseColors, colorSorting); IEnumerable reversedSortingColors = Data.ReverseColorSorting ? positionSortedColors.Reverse () : positionSortedColors; - ImmutableArray colors = reversedSortingColors.ToImmutableArray (); - Func distanceCalculator = GetDistanceCalculator (Data.DistanceCalculationMethod); + return new ( + w: dst.Width, + h: dst.Height, + points: points, + colors: reversedSortingColors.ToImmutableArray (), + distanceCalculator: GetDistanceCalculator (Data.DistanceCalculationMethod) + ); + } + + protected override void Render (ImageSurface src, ImageSurface dst, RectangleI roi) + { + VoronoiSettings settings = CreateSettings (dst, roi); Span dst_data = dst.GetPixelData (); for (int y = roi.Top; y <= roi.Bottom; y++) { - var dst_row = dst_data.Slice (y * w, w); + var dst_row = dst_data.Slice (y * settings.w, settings.w); for (int x = roi.Left; x <= roi.Right; x++) { PointI pixelLocation = new (x, y); double shortestDistance = double.MaxValue; int closestIndex = 0; - for (var i = 0; i < points.Length; i++) { - var point = points[i]; - double distance = distanceCalculator (point, pixelLocation); + for (var i = 0; i < settings.points.Length; i++) { + var point = settings.points[i]; + double distance = settings.distanceCalculator (point, pixelLocation); if (distance > shortestDistance) continue; shortestDistance = distance; closestIndex = i; } - dst_row[x] = colors[closestIndex]; + dst_row[x] = settings.colors[closestIndex]; } } } From 2dfe1a8a02588324ae4f17141d9c6c5d69894329 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Sun, 14 Jan 2024 19:37:54 +0100 Subject: [PATCH 13/31] Added option to show points --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index c11cd2bb5..96fd53b49 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -36,6 +36,7 @@ public override void LaunchConfiguration () private sealed record VoronoiSettings ( int w, int h, + bool showPoints, ImmutableArray points, ImmutableArray colors, Func distanceCalculator); @@ -54,6 +55,7 @@ private VoronoiSettings CreateSettings (ImageSurface dst, RectangleI roi) return new ( w: dst.Width, h: dst.Height, + showPoints: Data.ShowPoints, points: points, colors: reversedSortingColors.ToImmutableArray (), distanceCalculator: GetDistanceCalculator (Data.DistanceCalculationMethod) @@ -79,7 +81,10 @@ protected override void Render (ImageSurface src, ImageSurface dst, RectangleI r shortestDistance = distance; closestIndex = i; } - dst_row[x] = settings.colors[closestIndex]; + dst_row[x] = + settings.showPoints && shortestDistance == 0 + ? ColorBgra.Black + : settings.colors[closestIndex]; } } } @@ -208,6 +213,10 @@ public sealed class VoronoiDiagramData : EffectData [Caption ("Point Count"), MinimumValue (1), MaximumValue (1024)] public int PointCount { get; set; } = 100; + // Translators: The user can choose whether or not to render the points used in the calculation of a Voronoi diagram + [Caption ("Show Points")] + public bool ShowPoints { get; set; } = false; + [Caption ("Color Sorting")] public ColorSorting ColorSorting { get; set; } = ColorSorting.Random; From bd4babb890965111ccc632195cc8c1aa1f9cea4c Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Sun, 14 Jan 2024 19:52:26 +0100 Subject: [PATCH 14/31] Local refactoring and reformatting --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 138 ++++++++---------- 1 file changed, 58 insertions(+), 80 deletions(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index 96fd53b49..7b40fcc69 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -29,9 +29,7 @@ public VoronoiDiagramEffect () } public override void LaunchConfiguration () - { - EffectHelper.LaunchSimpleEffectDialog (this); - } + => EffectHelper.LaunchSimpleEffectDialog (this); private sealed record VoronoiSettings ( int w, @@ -90,59 +88,50 @@ protected override void Render (ImageSurface src, ImageSurface dst, RectangleI r } private static IEnumerable SortColors (IEnumerable baseColors, ColorSorting colorSorting) - { - switch (colorSorting) { - case ColorSorting.Random: - return baseColors; - case ColorSorting.HorizontalBGR: - case ColorSorting.VerticalBGR: - return baseColors.OrderBy (p => p.B).ThenBy (p => p.G).ThenBy (p => p.R); - case ColorSorting.HorizontalBRG: - case ColorSorting.VerticalBRG: - return baseColors.OrderBy (p => p.B).ThenBy (p => p.R).ThenBy (p => p.G); - case ColorSorting.HorizontalGBR: - case ColorSorting.VerticalGBR: - return baseColors.OrderBy (p => p.G).ThenBy (p => p.B).ThenBy (p => p.R); - case ColorSorting.HorizontalGRB: - case ColorSorting.VerticalGRB: - return baseColors.OrderBy (p => p.G).ThenBy (p => p.R).ThenBy (p => p.B); - case ColorSorting.HorizontalRBG: - case ColorSorting.VerticalRBG: - return baseColors.OrderBy (p => p.R).ThenBy (p => p.B).ThenBy (p => p.G); - case ColorSorting.HorizontalRGB: - case ColorSorting.VerticalRGB: - return baseColors.OrderBy (p => p.R).ThenBy (p => p.G).ThenBy (p => p.B); - default: - throw new InvalidEnumArgumentException (nameof (baseColors), (int) colorSorting, typeof (ColorSorting)); - } - } + => colorSorting switch { + + ColorSorting.Random => baseColors, + + ColorSorting.HorizontalBGR or ColorSorting.VerticalBGR => baseColors.OrderBy (p => p.B).ThenBy (p => p.G).ThenBy (p => p.R), + ColorSorting.HorizontalBRG or ColorSorting.VerticalBRG => baseColors.OrderBy (p => p.B).ThenBy (p => p.R).ThenBy (p => p.G), + + ColorSorting.HorizontalGBR or ColorSorting.VerticalGBR => baseColors.OrderBy (p => p.G).ThenBy (p => p.B).ThenBy (p => p.R), + ColorSorting.HorizontalGRB or ColorSorting.VerticalGRB => baseColors.OrderBy (p => p.G).ThenBy (p => p.R).ThenBy (p => p.B), + + ColorSorting.HorizontalRBG or ColorSorting.VerticalRBG => baseColors.OrderBy (p => p.R).ThenBy (p => p.B).ThenBy (p => p.G), + ColorSorting.HorizontalRGB or ColorSorting.VerticalRGB => baseColors.OrderBy (p => p.R).ThenBy (p => p.G).ThenBy (p => p.B), + + _ => throw new InvalidEnumArgumentException ( + nameof (baseColors), + (int) colorSorting, + typeof (ColorSorting)), + }; private static IEnumerable SortPoints (IEnumerable basePoints, ColorSorting colorSorting) { - switch (colorSorting) { - - case ColorSorting.Random: - return basePoints; - - case ColorSorting.HorizontalBGR: - case ColorSorting.HorizontalBRG: - case ColorSorting.HorizontalGBR: - case ColorSorting.HorizontalGRB: - case ColorSorting.HorizontalRBG: - case ColorSorting.HorizontalRGB: - return basePoints.OrderBy (p => p.X); - - case ColorSorting.VerticalBGR: - case ColorSorting.VerticalBRG: - case ColorSorting.VerticalGBR: - case ColorSorting.VerticalGRB: - case ColorSorting.VerticalRBG: - case ColorSorting.VerticalRGB: - return basePoints.OrderBy (p => p.Y); - - default: - throw new InvalidEnumArgumentException (nameof (colorSorting), (int) colorSorting, typeof (ColorSorting)); - } + return colorSorting switch { + + ColorSorting.Random => basePoints, + + ColorSorting.HorizontalBGR + or ColorSorting.HorizontalBRG + or ColorSorting.HorizontalGBR + or ColorSorting.HorizontalGRB + or ColorSorting.HorizontalRBG + or ColorSorting.HorizontalRGB => basePoints.OrderBy (p => p.X), + + ColorSorting.VerticalBGR + or ColorSorting.VerticalBRG + or ColorSorting.VerticalGBR + or ColorSorting.VerticalGRB + or ColorSorting.VerticalRBG + or ColorSorting.VerticalRGB => basePoints.OrderBy (p => p.Y), + + _ => throw new InvalidEnumArgumentException ( + nameof (colorSorting), + (int) colorSorting, + typeof (ColorSorting)), + }; } private static Func GetDistanceCalculator (DistanceCalculationMethod distanceCalculationMethod) @@ -151,7 +140,10 @@ private static Func GetDistanceCalculator (DistanceCalcu DistanceCalculationMethod.Euclidean => Euclidean, DistanceCalculationMethod.Manhattan => Manhattan, DistanceCalculationMethod.Chebyshev => Chebyshev, - _ => throw new InvalidEnumArgumentException (nameof (distanceCalculationMethod), (int) distanceCalculationMethod, typeof (DistanceCalculationMethod)), + _ => throw new InvalidEnumArgumentException ( + nameof (distanceCalculationMethod), + (int) distanceCalculationMethod, + typeof (DistanceCalculationMethod)), }; static double Euclidean (PointI targetPoint, PointI pixelLocation) @@ -243,55 +235,41 @@ public enum ColorSorting [Caption ("Random Color Sorting")] Random, - // Translators: Horizontal color sorting with B, then G as the leading terms - [Caption ("Horizontal (B, G, R)")] - HorizontalBGR, + [Caption ("Horizontal (B, G, R)")] HorizontalBGR, // Translators: Horizontal color sorting with B, then R as the leading terms - [Caption ("Horizontal (B, R, G)")] - HorizontalBRG, + [Caption ("Horizontal (B, R, G)")] HorizontalBRG, // Translators: Horizontal color sorting with G, then B as the leading terms - [Caption ("Horizontal (G, B, R)")] - HorizontalGBR, + [Caption ("Horizontal (G, B, R)")] HorizontalGBR, // Translators: Horizontal color sorting with G, then R as the leading terms - [Caption ("Horizontal (G, R, B)")] - HorizontalGRB, + [Caption ("Horizontal (G, R, B)")] HorizontalGRB, // Translators: Horizontal color sorting with R, then B as the leading terms - [Caption ("Horizontal (R, B, G)")] - HorizontalRBG, + [Caption ("Horizontal (R, B, G)")] HorizontalRBG, // Translators: Horizontal color sorting with R, then G as the leading terms - [Caption ("Horizontal (R, G, B)")] - HorizontalRGB, - + [Caption ("Horizontal (R, G, B)")] HorizontalRGB, // Translators: Vertical color sorting with B, then G as the leading terms - [Caption ("Vertical (B, G, R)")] - VerticalBGR, + [Caption ("Vertical (B, G, R)")] VerticalBGR, // Translators: Vertical color sorting with B, then R as the leading terms - [Caption ("Vertical (B, R, G)")] - VerticalBRG, + [Caption ("Vertical (B, R, G)")] VerticalBRG, // Translators: Vertical color sorting with G, then B as the leading terms - [Caption ("Vertical (G, B, R)")] - VerticalGBR, + [Caption ("Vertical (G, B, R)")] VerticalGBR, // Translators: Vertical color sorting with G, then R as the leading terms - [Caption ("Vertical (G, R, B)")] - VerticalGRB, + [Caption ("Vertical (G, R, B)")] VerticalGRB, // Translators: Vertical color sorting with R, then B as the leading terms - [Caption ("Vertical (R, B, G)")] - VerticalRBG, + [Caption ("Vertical (R, B, G)")] VerticalRBG, // Translators: Vertical color sorting with R, then G as the leading terms - [Caption ("Vertical (R, G, B)")] - VerticalRGB, + [Caption ("Vertical (R, G, B)")] VerticalRGB, } } From c00bec6885e0591e384473648a4d935ae2edf595 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Mon, 15 Jan 2024 00:13:45 +0100 Subject: [PATCH 15/31] Ordering of points Not leaving the ordering of non-leading component to chance --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index 7b40fcc69..a4b4b83bf 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -88,18 +88,28 @@ protected override void Render (ImageSurface src, ImageSurface dst, RectangleI r } private static IEnumerable SortColors (IEnumerable baseColors, ColorSorting colorSorting) + => colorSorting switch { ColorSorting.Random => baseColors, - ColorSorting.HorizontalBGR or ColorSorting.VerticalBGR => baseColors.OrderBy (p => p.B).ThenBy (p => p.G).ThenBy (p => p.R), - ColorSorting.HorizontalBRG or ColorSorting.VerticalBRG => baseColors.OrderBy (p => p.B).ThenBy (p => p.R).ThenBy (p => p.G), + ColorSorting.HorizontalBGR + or ColorSorting.VerticalBGR => baseColors.OrderBy (p => p.B).ThenBy (p => p.G).ThenBy (p => p.R), + + ColorSorting.HorizontalBRG + or ColorSorting.VerticalBRG => baseColors.OrderBy (p => p.B).ThenBy (p => p.R).ThenBy (p => p.G), + + ColorSorting.HorizontalGBR + or ColorSorting.VerticalGBR => baseColors.OrderBy (p => p.G).ThenBy (p => p.B).ThenBy (p => p.R), + + ColorSorting.HorizontalGRB + or ColorSorting.VerticalGRB => baseColors.OrderBy (p => p.G).ThenBy (p => p.R).ThenBy (p => p.B), - ColorSorting.HorizontalGBR or ColorSorting.VerticalGBR => baseColors.OrderBy (p => p.G).ThenBy (p => p.B).ThenBy (p => p.R), - ColorSorting.HorizontalGRB or ColorSorting.VerticalGRB => baseColors.OrderBy (p => p.G).ThenBy (p => p.R).ThenBy (p => p.B), + ColorSorting.HorizontalRBG + or ColorSorting.VerticalRBG => baseColors.OrderBy (p => p.R).ThenBy (p => p.B).ThenBy (p => p.G), - ColorSorting.HorizontalRBG or ColorSorting.VerticalRBG => baseColors.OrderBy (p => p.R).ThenBy (p => p.B).ThenBy (p => p.G), - ColorSorting.HorizontalRGB or ColorSorting.VerticalRGB => baseColors.OrderBy (p => p.R).ThenBy (p => p.G).ThenBy (p => p.B), + ColorSorting.HorizontalRGB + or ColorSorting.VerticalRGB => baseColors.OrderBy (p => p.R).ThenBy (p => p.G).ThenBy (p => p.B), _ => throw new InvalidEnumArgumentException ( nameof (baseColors), @@ -108,8 +118,8 @@ private static IEnumerable SortColors (IEnumerable baseCol }; private static IEnumerable SortPoints (IEnumerable basePoints, ColorSorting colorSorting) - { - return colorSorting switch { + + => colorSorting switch { ColorSorting.Random => basePoints, @@ -118,21 +128,20 @@ or ColorSorting.HorizontalBRG or ColorSorting.HorizontalGBR or ColorSorting.HorizontalGRB or ColorSorting.HorizontalRBG - or ColorSorting.HorizontalRGB => basePoints.OrderBy (p => p.X), + or ColorSorting.HorizontalRGB => basePoints.OrderBy (p => p.X).ThenBy (p => p.Y), ColorSorting.VerticalBGR or ColorSorting.VerticalBRG or ColorSorting.VerticalGBR or ColorSorting.VerticalGRB or ColorSorting.VerticalRBG - or ColorSorting.VerticalRGB => basePoints.OrderBy (p => p.Y), + or ColorSorting.VerticalRGB => basePoints.OrderBy (p => p.Y).ThenBy (p => p.X), _ => throw new InvalidEnumArgumentException ( nameof (colorSorting), (int) colorSorting, typeof (ColorSorting)), }; - } private static Func GetDistanceCalculator (DistanceCalculationMethod distanceCalculationMethod) { From 3fa9d8f92d96326cc44cecfd68df8fff3f615e04 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Mon, 15 Jan 2024 10:21:51 +0100 Subject: [PATCH 16/31] Changed naming and added TODO comment as per suggestion --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index a4b4b83bf..4e64f1ec3 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -43,10 +43,10 @@ private VoronoiSettings CreateSettings (ImageSurface dst, RectangleI roi) { ColorSorting colorSorting = Data.ColorSorting; - IEnumerable basePoints = CreatePoints (roi, Data.PointCount, Data.PointLocationsSeed); + IEnumerable basePoints = CreatePoints (roi, Data.NumberOfCells, Data.RandomLocations); ImmutableArray points = SortPoints (basePoints, colorSorting).ToImmutableArray (); - IEnumerable baseColors = CreateColors (points.Length, Data.ColorsSeed); + IEnumerable baseColors = CreateColors (points.Length, Data.RandomColors); IEnumerable positionSortedColors = SortColors (baseColors, colorSorting); IEnumerable reversedSortingColors = Data.ReverseColorSorting ? positionSortedColors.Reverse () : positionSortedColors; @@ -56,7 +56,7 @@ private VoronoiSettings CreateSettings (ImageSurface dst, RectangleI roi) showPoints: Data.ShowPoints, points: points, colors: reversedSortingColors.ToImmutableArray (), - distanceCalculator: GetDistanceCalculator (Data.DistanceCalculationMethod) + distanceCalculator: GetDistanceCalculator (Data.DistanceMetric) ); } @@ -73,6 +73,9 @@ protected override void Render (ImageSurface src, ImageSurface dst, RectangleI r double shortestDistance = double.MaxValue; int closestIndex = 0; for (var i = 0; i < settings.points.Length; i++) { + // TODO: Acceleration structure that limits the search + // to a relevant subset of points, for better performance. + // Some ideas to consider: quadtree, spatial hashing var point = settings.points[i]; double distance = settings.distanceCalculator (point, pixelLocation); if (distance > shortestDistance) continue; @@ -143,16 +146,16 @@ or ColorSorting.VerticalRBG typeof (ColorSorting)), }; - private static Func GetDistanceCalculator (DistanceCalculationMethod distanceCalculationMethod) + private static Func GetDistanceCalculator (DistanceMetric distanceCalculationMethod) { return distanceCalculationMethod switch { - DistanceCalculationMethod.Euclidean => Euclidean, - DistanceCalculationMethod.Manhattan => Manhattan, - DistanceCalculationMethod.Chebyshev => Chebyshev, + DistanceMetric.Euclidean => Euclidean, + DistanceMetric.Manhattan => Manhattan, + DistanceMetric.Chebyshev => Chebyshev, _ => throw new InvalidEnumArgumentException ( nameof (distanceCalculationMethod), (int) distanceCalculationMethod, - typeof (DistanceCalculationMethod)), + typeof (DistanceMetric)), }; static double Euclidean (PointI targetPoint, PointI pixelLocation) @@ -208,11 +211,11 @@ private static IEnumerable CreateColors (int colorCount, RandomSeed c public sealed class VoronoiDiagramData : EffectData { - [Caption ("Distance Calculation Method")] - public DistanceCalculationMethod DistanceCalculationMethod { get; set; } = DistanceCalculationMethod.Euclidean; + [Caption ("Distance Metric")] + public DistanceMetric DistanceMetric { get; set; } = DistanceMetric.Euclidean; - [Caption ("Point Count"), MinimumValue (1), MaximumValue (1024)] - public int PointCount { get; set; } = 100; + [Caption ("Number of Cells"), MinimumValue (1), MaximumValue (1024)] + public int NumberOfCells { get; set; } = 100; // Translators: The user can choose whether or not to render the points used in the calculation of a Voronoi diagram [Caption ("Show Points")] @@ -225,14 +228,14 @@ public sealed class VoronoiDiagramData : EffectData [Caption ("Reverse Color Sorting")] public bool ReverseColorSorting { get; set; } = false; - [Caption ("Colors Seed")] - public RandomSeed ColorsSeed { get; set; } = new (0); + [Caption ("Random Colors")] + public RandomSeed RandomColors { get; set; } = new (0); - [Caption ("Point Locations Seed")] - public RandomSeed PointLocationsSeed { get; set; } = new (0); + [Caption ("Random Locations")] + public RandomSeed RandomLocations { get; set; } = new (0); } - public enum DistanceCalculationMethod + public enum DistanceMetric { Euclidean, Manhattan, From 4955709d259f970c956b53febdcfd36e6cf78f43 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Mon, 15 Jan 2024 10:39:25 +0100 Subject: [PATCH 17/31] Added tests --- tests/Pinta.Effects.Tests/Assets/voronoi1.png | Bin 0 -> 8169 bytes tests/Pinta.Effects.Tests/Assets/voronoi2.png | Bin 0 -> 12199 bytes tests/Pinta.Effects.Tests/Assets/voronoi3.png | Bin 0 -> 7103 bytes tests/Pinta.Effects.Tests/Assets/voronoi4.png | Bin 0 -> 8058 bytes tests/Pinta.Effects.Tests/EffectsTest.cs | 34 +++++++++++++++++- 5 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 tests/Pinta.Effects.Tests/Assets/voronoi1.png create mode 100644 tests/Pinta.Effects.Tests/Assets/voronoi2.png create mode 100644 tests/Pinta.Effects.Tests/Assets/voronoi3.png create mode 100644 tests/Pinta.Effects.Tests/Assets/voronoi4.png diff --git a/tests/Pinta.Effects.Tests/Assets/voronoi1.png b/tests/Pinta.Effects.Tests/Assets/voronoi1.png new file mode 100644 index 0000000000000000000000000000000000000000..9789618e992bb24d8c1167ef72707bbed4b114a8 GIT binary patch literal 8169 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 literal 0 HcmV?d00001 diff --git a/tests/Pinta.Effects.Tests/Assets/voronoi3.png b/tests/Pinta.Effects.Tests/Assets/voronoi3.png new file mode 100644 index 0000000000000000000000000000000000000000..acbe0e16de7591595ced4aaa5ac24c4d61e02c3c GIT binary patch literal 7103 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 literal 0 HcmV?d00001 diff --git a/tests/Pinta.Effects.Tests/Assets/voronoi4.png b/tests/Pinta.Effects.Tests/Assets/voronoi4.png new file mode 100644 index 0000000000000000000000000000000000000000..748bf8f2a868956c21477276ddbb2a7f8dfe8bd7 GIT binary patch literal 8058 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 literal 0 HcmV?d00001 diff --git a/tests/Pinta.Effects.Tests/EffectsTest.cs b/tests/Pinta.Effects.Tests/EffectsTest.cs index 6b33f7859..d41eaa77d 100644 --- a/tests/Pinta.Effects.Tests/EffectsTest.cs +++ b/tests/Pinta.Effects.Tests/EffectsTest.cs @@ -321,7 +321,7 @@ public void Pixelate2 () [Test] [Ignore ("Depends on PintaCore being initialized")] - public void PolarInversion () + public void PolarInversion1 () { // TODO } @@ -454,6 +454,38 @@ public void Unfocus2 () Utilities.TestEffect (effect, "unfocus2.png"); } + [Test] + public void Voronoi1 () + { + var effect = new VoronoiDiagramEffect (); + effect.Data.NumberOfCells = 100; + Utilities.TestEffect (effect, "voronoi1.png"); + } + + [Test] + public void Voronoi2 () + { + var effect = new VoronoiDiagramEffect (); + effect.Data.NumberOfCells = 200; + Utilities.TestEffect (effect, "voronoi2.png"); + } + + [Test] + public void Voronoi3 () + { + var effect = new VoronoiDiagramEffect (); + effect.Data.DistanceMetric = VoronoiDiagramEffect.DistanceMetric.Manhattan; + Utilities.TestEffect (effect, "voronoi3.png"); + } + + [Test] + public void Voronoi4 () + { + var effect = new VoronoiDiagramEffect (); + effect.Data.ColorSorting = VoronoiDiagramEffect.ColorSorting.HorizontalBGR; + Utilities.TestEffect (effect, "voronoi4.png"); + } + [Test] public void ZoomBlur1 () { From 53f5bcbae13edb1e3e4d8fd026b3aa6171ab3568 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Mon, 15 Jan 2024 10:42:07 +0100 Subject: [PATCH 18/31] Removed property assignment --- tests/Pinta.Effects.Tests/EffectsTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Pinta.Effects.Tests/EffectsTest.cs b/tests/Pinta.Effects.Tests/EffectsTest.cs index d41eaa77d..182e5e4da 100644 --- a/tests/Pinta.Effects.Tests/EffectsTest.cs +++ b/tests/Pinta.Effects.Tests/EffectsTest.cs @@ -458,7 +458,6 @@ public void Unfocus2 () public void Voronoi1 () { var effect = new VoronoiDiagramEffect (); - effect.Data.NumberOfCells = 100; Utilities.TestEffect (effect, "voronoi1.png"); } From 1aab816d09ec91d7bedf4aa6375a3f4a9b253f9c Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Mon, 15 Jan 2024 18:23:50 +0100 Subject: [PATCH 19/31] Constructor now gets a service manager --- Pinta.Effects/CoreEffectsExtension.cs | 2 +- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 2 +- tests/Pinta.Effects.Tests/EffectsTest.cs | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Pinta.Effects/CoreEffectsExtension.cs b/Pinta.Effects/CoreEffectsExtension.cs index 481281a4e..d42b797a1 100644 --- a/Pinta.Effects/CoreEffectsExtension.cs +++ b/Pinta.Effects/CoreEffectsExtension.cs @@ -84,7 +84,7 @@ public void Initialize () PintaCore.Effects.RegisterEffect (new TileEffect ()); PintaCore.Effects.RegisterEffect (new TwistEffect ()); PintaCore.Effects.RegisterEffect (new UnfocusEffect ()); - PintaCore.Effects.RegisterEffect (new VoronoiDiagramEffect ()); + PintaCore.Effects.RegisterEffect (new VoronoiDiagramEffect (services)); PintaCore.Effects.RegisterEffect (new ZoomBlurEffect ()); } diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index 4e64f1ec3..8bb958bd1 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -23,7 +23,7 @@ public sealed class VoronoiDiagramEffect : BaseEffect public VoronoiDiagramData Data => (VoronoiDiagramData) EffectData!; // NRT - Set in constructor - public VoronoiDiagramEffect () + public VoronoiDiagramEffect (IServiceManager _) { EffectData = new VoronoiDiagramData (); } diff --git a/tests/Pinta.Effects.Tests/EffectsTest.cs b/tests/Pinta.Effects.Tests/EffectsTest.cs index 182e5e4da..7b1ef7f5b 100644 --- a/tests/Pinta.Effects.Tests/EffectsTest.cs +++ b/tests/Pinta.Effects.Tests/EffectsTest.cs @@ -457,14 +457,14 @@ public void Unfocus2 () [Test] public void Voronoi1 () { - var effect = new VoronoiDiagramEffect (); + var effect = new VoronoiDiagramEffect (Utilities.CreateMockServices ()); Utilities.TestEffect (effect, "voronoi1.png"); } [Test] public void Voronoi2 () { - var effect = new VoronoiDiagramEffect (); + var effect = new VoronoiDiagramEffect (Utilities.CreateMockServices ()); effect.Data.NumberOfCells = 200; Utilities.TestEffect (effect, "voronoi2.png"); } @@ -472,7 +472,7 @@ public void Voronoi2 () [Test] public void Voronoi3 () { - var effect = new VoronoiDiagramEffect (); + var effect = new VoronoiDiagramEffect (Utilities.CreateMockServices ()); effect.Data.DistanceMetric = VoronoiDiagramEffect.DistanceMetric.Manhattan; Utilities.TestEffect (effect, "voronoi3.png"); } @@ -480,7 +480,7 @@ public void Voronoi3 () [Test] public void Voronoi4 () { - var effect = new VoronoiDiagramEffect (); + var effect = new VoronoiDiagramEffect (Utilities.CreateMockServices ()); effect.Data.ColorSorting = VoronoiDiagramEffect.ColorSorting.HorizontalBGR; Utilities.TestEffect (effect, "voronoi4.png"); } From 38ca138a226dbc6411b91b240f57a83326f6be9a Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Tue, 16 Jan 2024 14:39:06 +0100 Subject: [PATCH 20/31] Created `Utility.GeneratePixelOffsets` --- Pinta.Core/Effects/Utility.cs | 17 +++++++++ Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 38 ++++++++----------- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/Pinta.Core/Effects/Utility.cs b/Pinta.Core/Effects/Utility.cs index 4fd6c3763..dce8cfac8 100644 --- a/Pinta.Core/Effects/Utility.cs +++ b/Pinta.Core/Effects/Utility.cs @@ -6,6 +6,7 @@ ///////////////////////////////////////////////////////////////////////////////// using System; +using System.Collections.Generic; using System.Numerics; using System.Reflection; @@ -47,6 +48,22 @@ public static double Magnitude (this PointI point) public static double Distance (this PointD origin, in PointD dest) => Magnitude (origin - dest); + public readonly record struct PixelOffset (PointI coordinates, int memoryOffset); + + /// + /// Offsets of pixels, if we consider all pixels in a canvas of + /// size to be sequential in memory + /// (from left to right, and top to bottom) + /// + public static IEnumerable GeneratePixelOffsets (this RectangleI roi, Size canvasSize) + { + for (int y = roi.Top; y <= roi.Bottom; y++) + for (int x = roi.Left; x <= roi.Right; x++) + yield return new ( + coordinates: new (x, y), + memoryOffset: (y * canvasSize.Width) + x); + } + /// /// Difference between upper and lower bounds is zero /// diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index 8bb958bd1..d8025c2fe 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -32,8 +32,7 @@ public override void LaunchConfiguration () => EffectHelper.LaunchSimpleEffectDialog (this); private sealed record VoronoiSettings ( - int w, - int h, + Size size, bool showPoints, ImmutableArray points, ImmutableArray colors, @@ -51,8 +50,7 @@ private VoronoiSettings CreateSettings (ImageSurface dst, RectangleI roi) IEnumerable reversedSortingColors = Data.ReverseColorSorting ? positionSortedColors.Reverse () : positionSortedColors; return new ( - w: dst.Width, - h: dst.Height, + size: dst.GetSize (), showPoints: Data.ShowPoints, points: points, colors: reversedSortingColors.ToImmutableArray (), @@ -66,27 +64,23 @@ protected override void Render (ImageSurface src, ImageSurface dst, RectangleI r Span dst_data = dst.GetPixelData (); - for (int y = roi.Top; y <= roi.Bottom; y++) { - var dst_row = dst_data.Slice (y * settings.w, settings.w); - for (int x = roi.Left; x <= roi.Right; x++) { - PointI pixelLocation = new (x, y); - double shortestDistance = double.MaxValue; - int closestIndex = 0; - for (var i = 0; i < settings.points.Length; i++) { - // TODO: Acceleration structure that limits the search - // to a relevant subset of points, for better performance. - // Some ideas to consider: quadtree, spatial hashing - var point = settings.points[i]; - double distance = settings.distanceCalculator (point, pixelLocation); - if (distance > shortestDistance) continue; - shortestDistance = distance; - closestIndex = i; - } - dst_row[x] = + foreach (var pixel in roi.GeneratePixelOffsets (settings.size)) { + double shortestDistance = double.MaxValue; + int closestIndex = 0; + for (var i = 0; i < settings.points.Length; i++) { + // TODO: Acceleration structure that limits the search + // to a relevant subset of points, for better performance. + // Some ideas to consider: quadtree, spatial hashing + var point = settings.points[i]; + double distance = settings.distanceCalculator (point, pixel.coordinates); + if (distance > shortestDistance) continue; + shortestDistance = distance; + closestIndex = i; + } + dst_data[pixel.memoryOffset] = settings.showPoints && shortestDistance == 0 ? ColorBgra.Black : settings.colors[closestIndex]; - } } } From 73946e6a7f1eb9e41412df65006bc9108f30beb3 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:11:04 +0100 Subject: [PATCH 21/31] Creating pixels in parallel --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index d8025c2fe..b34f7feba 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Linq; +using System.Threading.Tasks; using Cairo; using Pinta.Core; using Pinta.Gui.Widgets; @@ -61,12 +63,21 @@ private VoronoiSettings CreateSettings (ImageSurface dst, RectangleI roi) protected override void Render (ImageSurface src, ImageSurface dst, RectangleI roi) { VoronoiSettings settings = CreateSettings (dst, roi); - + var changedPixels = CreateColors (roi, settings); Span dst_data = dst.GetPixelData (); + foreach (var kvp in changedPixels) + dst_data[kvp.Key] = kvp.Value; + } - foreach (var pixel in roi.GeneratePixelOffsets (settings.size)) { + private static IReadOnlyDictionary CreateColors (RectangleI roi, VoronoiSettings settings) + { + ConcurrentDictionary changedPixels = new (); + + void AssignParallel (Utility.PixelOffset pixel) + { double shortestDistance = double.MaxValue; int closestIndex = 0; + for (var i = 0; i < settings.points.Length; i++) { // TODO: Acceleration structure that limits the search // to a relevant subset of points, for better performance. @@ -77,11 +88,18 @@ protected override void Render (ImageSurface src, ImageSurface dst, RectangleI r shortestDistance = distance; closestIndex = i; } - dst_data[pixel.memoryOffset] = - settings.showPoints && shortestDistance == 0 - ? ColorBgra.Black - : settings.colors[closestIndex]; + + ColorBgra finalColor = + settings.showPoints && shortestDistance == 0 + ? ColorBgra.Black + : settings.colors[closestIndex]; + + changedPixels[pixel.memoryOffset] = finalColor; } + + Parallel.ForEach (roi.GeneratePixelOffsets (settings.size), AssignParallel); + + return changedPixels; } private static IEnumerable SortColors (IEnumerable baseColors, ColorSorting colorSorting) From 4fa83a64b079e0ce4953d24640863b25b1e19ec4 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Tue, 16 Jan 2024 23:26:41 +0100 Subject: [PATCH 22/31] Removed intermediate dictionary structure and added check --- Pinta.Core/Effects/Utility.cs | 9 +++++++-- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 16 +++------------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/Pinta.Core/Effects/Utility.cs b/Pinta.Core/Effects/Utility.cs index dce8cfac8..6fa7c5bf7 100644 --- a/Pinta.Core/Effects/Utility.cs +++ b/Pinta.Core/Effects/Utility.cs @@ -57,11 +57,16 @@ public static double Distance (this PointD origin, in PointD dest) /// public static IEnumerable GeneratePixelOffsets (this RectangleI roi, Size canvasSize) { - for (int y = roi.Top; y <= roi.Bottom; y++) + if (roi.Left < 0 || roi.Right >= canvasSize.Width || roi.Top < 0 || roi.Bottom >= canvasSize.Height) + throw new ArgumentException ($"Rectangle is out of size bounds"); + + for (int y = roi.Top; y <= roi.Bottom; y++) { + int rowOffset = y * canvasSize.Width; for (int x = roi.Left; x <= roi.Right; x++) yield return new ( coordinates: new (x, y), - memoryOffset: (y * canvasSize.Width) + x); + memoryOffset: rowOffset + x); + } } /// diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index b34f7feba..73cb0ab2f 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -63,17 +63,11 @@ private VoronoiSettings CreateSettings (ImageSurface dst, RectangleI roi) protected override void Render (ImageSurface src, ImageSurface dst, RectangleI roi) { VoronoiSettings settings = CreateSettings (dst, roi); - var changedPixels = CreateColors (roi, settings); Span dst_data = dst.GetPixelData (); - foreach (var kvp in changedPixels) + foreach (var kvp in roi.GeneratePixelOffsets (settings.size).Select (CreateColor).AsParallel ()) dst_data[kvp.Key] = kvp.Value; - } - - private static IReadOnlyDictionary CreateColors (RectangleI roi, VoronoiSettings settings) - { - ConcurrentDictionary changedPixels = new (); - void AssignParallel (Utility.PixelOffset pixel) + KeyValuePair CreateColor (Utility.PixelOffset pixel) { double shortestDistance = double.MaxValue; int closestIndex = 0; @@ -94,12 +88,8 @@ void AssignParallel (Utility.PixelOffset pixel) ? ColorBgra.Black : settings.colors[closestIndex]; - changedPixels[pixel.memoryOffset] = finalColor; + return KeyValuePair.Create (pixel.memoryOffset, finalColor); } - - Parallel.ForEach (roi.GeneratePixelOffsets (settings.size), AssignParallel); - - return changedPixels; } private static IEnumerable SortColors (IEnumerable baseColors, ColorSorting colorSorting) From d85079e7d45ba42b0a86d79954a4052309f9ddbd Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Tue, 16 Jan 2024 23:38:27 +0100 Subject: [PATCH 23/31] Revert "Removed intermediate dictionary structure and added check" This reverts commit 4fa83a64b079e0ce4953d24640863b25b1e19ec4. --- Pinta.Core/Effects/Utility.cs | 9 ++------- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Pinta.Core/Effects/Utility.cs b/Pinta.Core/Effects/Utility.cs index 6fa7c5bf7..dce8cfac8 100644 --- a/Pinta.Core/Effects/Utility.cs +++ b/Pinta.Core/Effects/Utility.cs @@ -57,16 +57,11 @@ public static double Distance (this PointD origin, in PointD dest) /// public static IEnumerable GeneratePixelOffsets (this RectangleI roi, Size canvasSize) { - if (roi.Left < 0 || roi.Right >= canvasSize.Width || roi.Top < 0 || roi.Bottom >= canvasSize.Height) - throw new ArgumentException ($"Rectangle is out of size bounds"); - - for (int y = roi.Top; y <= roi.Bottom; y++) { - int rowOffset = y * canvasSize.Width; + for (int y = roi.Top; y <= roi.Bottom; y++) for (int x = roi.Left; x <= roi.Right; x++) yield return new ( coordinates: new (x, y), - memoryOffset: rowOffset + x); - } + memoryOffset: (y * canvasSize.Width) + x); } /// diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index 73cb0ab2f..b34f7feba 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -63,11 +63,17 @@ private VoronoiSettings CreateSettings (ImageSurface dst, RectangleI roi) protected override void Render (ImageSurface src, ImageSurface dst, RectangleI roi) { VoronoiSettings settings = CreateSettings (dst, roi); + var changedPixels = CreateColors (roi, settings); Span dst_data = dst.GetPixelData (); - foreach (var kvp in roi.GeneratePixelOffsets (settings.size).Select (CreateColor).AsParallel ()) + foreach (var kvp in changedPixels) dst_data[kvp.Key] = kvp.Value; + } + + private static IReadOnlyDictionary CreateColors (RectangleI roi, VoronoiSettings settings) + { + ConcurrentDictionary changedPixels = new (); - KeyValuePair CreateColor (Utility.PixelOffset pixel) + void AssignParallel (Utility.PixelOffset pixel) { double shortestDistance = double.MaxValue; int closestIndex = 0; @@ -88,8 +94,12 @@ KeyValuePair CreateColor (Utility.PixelOffset pixel) ? ColorBgra.Black : settings.colors[closestIndex]; - return KeyValuePair.Create (pixel.memoryOffset, finalColor); + changedPixels[pixel.memoryOffset] = finalColor; } + + Parallel.ForEach (roi.GeneratePixelOffsets (settings.size), AssignParallel); + + return changedPixels; } private static IEnumerable SortColors (IEnumerable baseColors, ColorSorting colorSorting) From 63ed0cd8963c1b60801a1fcd75f1f7dbb866e6b4 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Tue, 16 Jan 2024 23:38:56 +0100 Subject: [PATCH 24/31] With the dictionary it seems to work faster for some reason --- Pinta.Core/Effects/Utility.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Pinta.Core/Effects/Utility.cs b/Pinta.Core/Effects/Utility.cs index dce8cfac8..6fa7c5bf7 100644 --- a/Pinta.Core/Effects/Utility.cs +++ b/Pinta.Core/Effects/Utility.cs @@ -57,11 +57,16 @@ public static double Distance (this PointD origin, in PointD dest) /// public static IEnumerable GeneratePixelOffsets (this RectangleI roi, Size canvasSize) { - for (int y = roi.Top; y <= roi.Bottom; y++) + if (roi.Left < 0 || roi.Right >= canvasSize.Width || roi.Top < 0 || roi.Bottom >= canvasSize.Height) + throw new ArgumentException ($"Rectangle is out of size bounds"); + + for (int y = roi.Top; y <= roi.Bottom; y++) { + int rowOffset = y * canvasSize.Width; for (int x = roi.Left; x <= roi.Right; x++) yield return new ( coordinates: new (x, y), - memoryOffset: (y * canvasSize.Width) + x); + memoryOffset: rowOffset + x); + } } /// From 44b3fbacf11a85d8aa9deb22eba6637f17db027e Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Wed, 17 Jan 2024 00:02:26 +0100 Subject: [PATCH 25/31] Tried parallel (and removing intermediate structure) in some other way --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index b34f7feba..ef074eaba 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -63,17 +63,11 @@ private VoronoiSettings CreateSettings (ImageSurface dst, RectangleI roi) protected override void Render (ImageSurface src, ImageSurface dst, RectangleI roi) { VoronoiSettings settings = CreateSettings (dst, roi); - var changedPixels = CreateColors (roi, settings); Span dst_data = dst.GetPixelData (); - foreach (var kvp in changedPixels) + foreach (var kvp in roi.GeneratePixelOffsets (settings.size).AsParallel ().Select (CreateColor)) dst_data[kvp.Key] = kvp.Value; - } - - private static IReadOnlyDictionary CreateColors (RectangleI roi, VoronoiSettings settings) - { - ConcurrentDictionary changedPixels = new (); - void AssignParallel (Utility.PixelOffset pixel) + KeyValuePair CreateColor (Utility.PixelOffset pixel) { double shortestDistance = double.MaxValue; int closestIndex = 0; @@ -94,12 +88,8 @@ void AssignParallel (Utility.PixelOffset pixel) ? ColorBgra.Black : settings.colors[closestIndex]; - changedPixels[pixel.memoryOffset] = finalColor; + return KeyValuePair.Create (pixel.memoryOffset, finalColor); } - - Parallel.ForEach (roi.GeneratePixelOffsets (settings.size), AssignParallel); - - return changedPixels; } private static IEnumerable SortColors (IEnumerable baseColors, ColorSorting colorSorting) From c1a2ce98a5f007e977d2d2aed0e3e34bae23446e Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Wed, 17 Jan 2024 11:18:49 +0100 Subject: [PATCH 26/31] A few adjustments --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index ef074eaba..efc414a29 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -1,10 +1,8 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Linq; -using System.Threading.Tasks; using Cairo; using Pinta.Core; using Pinta.Gui.Widgets; @@ -44,7 +42,7 @@ private VoronoiSettings CreateSettings (ImageSurface dst, RectangleI roi) { ColorSorting colorSorting = Data.ColorSorting; - IEnumerable basePoints = CreatePoints (roi, Data.NumberOfCells, Data.RandomLocations); + IEnumerable basePoints = CreatePoints (roi, Data.NumberOfCells, Data.RandomPointLocations); ImmutableArray points = SortPoints (basePoints, colorSorting).ToImmutableArray (); IEnumerable baseColors = CreateColors (points.Length, Data.RandomColors); @@ -233,8 +231,8 @@ public sealed class VoronoiDiagramData : EffectData [Caption ("Random Colors")] public RandomSeed RandomColors { get; set; } = new (0); - [Caption ("Random Locations")] - public RandomSeed RandomLocations { get; set; } = new (0); + [Caption ("Random Point Locations")] + public RandomSeed RandomPointLocations { get; set; } = new (0); } public enum DistanceMetric From 313f7ee20ac0e3a04eda594df82d8c929b8e17a6 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Thu, 18 Jan 2024 12:35:11 +0100 Subject: [PATCH 27/31] Added chrome service to `VoronoiDiagramEffect` --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index efc414a29..8c9a4eb18 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -23,13 +23,16 @@ public sealed class VoronoiDiagramEffect : BaseEffect public VoronoiDiagramData Data => (VoronoiDiagramData) EffectData!; // NRT - Set in constructor - public VoronoiDiagramEffect (IServiceManager _) + private readonly IChromeService chrome; + + public VoronoiDiagramEffect (IServiceManager services) { + chrome = services.GetService (); EffectData = new VoronoiDiagramData (); } public override void LaunchConfiguration () - => EffectHelper.LaunchSimpleEffectDialog (this); + => chrome.LaunchSimpleEffectDialog (this); private sealed record VoronoiSettings ( Size size, From 0f772d2112154d429741a25cf5e7d58f8cd1c550 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Fri, 19 Jan 2024 01:31:59 +0100 Subject: [PATCH 28/31] Reduced number of sortings --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 81 +++++-------------- 1 file changed, 21 insertions(+), 60 deletions(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index 8c9a4eb18..47e44faed 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -99,23 +99,9 @@ private static IEnumerable SortColors (IEnumerable baseCol ColorSorting.Random => baseColors, - ColorSorting.HorizontalBGR - or ColorSorting.VerticalBGR => baseColors.OrderBy (p => p.B).ThenBy (p => p.G).ThenBy (p => p.R), - - ColorSorting.HorizontalBRG - or ColorSorting.VerticalBRG => baseColors.OrderBy (p => p.B).ThenBy (p => p.R).ThenBy (p => p.G), - - ColorSorting.HorizontalGBR - or ColorSorting.VerticalGBR => baseColors.OrderBy (p => p.G).ThenBy (p => p.B).ThenBy (p => p.R), - - ColorSorting.HorizontalGRB - or ColorSorting.VerticalGRB => baseColors.OrderBy (p => p.G).ThenBy (p => p.R).ThenBy (p => p.B), - - ColorSorting.HorizontalRBG - or ColorSorting.VerticalRBG => baseColors.OrderBy (p => p.R).ThenBy (p => p.B).ThenBy (p => p.G), - - ColorSorting.HorizontalRGB - or ColorSorting.VerticalRGB => baseColors.OrderBy (p => p.R).ThenBy (p => p.G).ThenBy (p => p.B), + ColorSorting.HorizontalB or ColorSorting.VerticalB => baseColors.OrderBy (p => p.B), + ColorSorting.HorizontalG or ColorSorting.VerticalG => baseColors.OrderBy (p => p.G), + ColorSorting.HorizontalR or ColorSorting.VerticalR => baseColors.OrderBy (p => p.R), _ => throw new InvalidEnumArgumentException ( nameof (baseColors), @@ -129,19 +115,13 @@ private static IEnumerable SortPoints (IEnumerable basePoints, C ColorSorting.Random => basePoints, - ColorSorting.HorizontalBGR - or ColorSorting.HorizontalBRG - or ColorSorting.HorizontalGBR - or ColorSorting.HorizontalGRB - or ColorSorting.HorizontalRBG - or ColorSorting.HorizontalRGB => basePoints.OrderBy (p => p.X).ThenBy (p => p.Y), + ColorSorting.HorizontalB + or ColorSorting.HorizontalG + or ColorSorting.HorizontalR => basePoints.OrderBy (p => p.X).ThenBy (p => p.Y), - ColorSorting.VerticalBGR - or ColorSorting.VerticalBRG - or ColorSorting.VerticalGBR - or ColorSorting.VerticalGRB - or ColorSorting.VerticalRBG - or ColorSorting.VerticalRGB => basePoints.OrderBy (p => p.Y).ThenBy (p => p.X), + ColorSorting.VerticalB + or ColorSorting.VerticalG + or ColorSorting.VerticalR => basePoints.OrderBy (p => p.Y).ThenBy (p => p.X), _ => throw new InvalidEnumArgumentException ( nameof (colorSorting), @@ -249,42 +229,23 @@ public enum ColorSorting { [Caption ("Random Color Sorting")] Random, + // Translators: Horizontal color sorting with B as the leading term + [Caption ("Horizontal (B)")] HorizontalB, - // Translators: Horizontal color sorting with B, then G as the leading terms - [Caption ("Horizontal (B, G, R)")] HorizontalBGR, - - // Translators: Horizontal color sorting with B, then R as the leading terms - [Caption ("Horizontal (B, R, G)")] HorizontalBRG, - - // Translators: Horizontal color sorting with G, then B as the leading terms - [Caption ("Horizontal (G, B, R)")] HorizontalGBR, - - // Translators: Horizontal color sorting with G, then R as the leading terms - [Caption ("Horizontal (G, R, B)")] HorizontalGRB, - - // Translators: Horizontal color sorting with R, then B as the leading terms - [Caption ("Horizontal (R, B, G)")] HorizontalRBG, - - // Translators: Horizontal color sorting with R, then G as the leading terms - [Caption ("Horizontal (R, G, B)")] HorizontalRGB, - - - // Translators: Vertical color sorting with B, then G as the leading terms - [Caption ("Vertical (B, G, R)")] VerticalBGR, + // Translators: Horizontal color sorting with G as the leading term + [Caption ("Horizontal (G)")] HorizontalG, - // Translators: Vertical color sorting with B, then R as the leading terms - [Caption ("Vertical (B, R, G)")] VerticalBRG, + // Translators: Horizontal color sorting with R as the leading term + [Caption ("Horizontal (R)")] HorizontalR, - // Translators: Vertical color sorting with G, then B as the leading terms - [Caption ("Vertical (G, B, R)")] VerticalGBR, - // Translators: Vertical color sorting with G, then R as the leading terms - [Caption ("Vertical (G, R, B)")] VerticalGRB, + // Translators: Vertical color sorting with B as the leading term + [Caption ("Vertical (B)")] VerticalB, - // Translators: Vertical color sorting with R, then B as the leading terms - [Caption ("Vertical (R, B, G)")] VerticalRBG, + // Translators: Vertical color sorting with G as the leading term + [Caption ("Vertical (G)")] VerticalG, - // Translators: Vertical color sorting with R, then G as the leading terms - [Caption ("Vertical (R, G, B)")] VerticalRGB, + // Translators: Vertical color sorting with R as the leading term + [Caption ("Vertical (R)")] VerticalR, } } From aa26e5a6b564db0af451e2a1f1f22aefd0fdaf88 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Fri, 19 Jan 2024 01:34:55 +0100 Subject: [PATCH 29/31] Removed a test --- tests/Pinta.Effects.Tests/Assets/voronoi4.png | Bin 8058 -> 0 bytes tests/Pinta.Effects.Tests/EffectsTest.cs | 8 -------- 2 files changed, 8 deletions(-) delete mode 100644 tests/Pinta.Effects.Tests/Assets/voronoi4.png diff --git a/tests/Pinta.Effects.Tests/Assets/voronoi4.png b/tests/Pinta.Effects.Tests/Assets/voronoi4.png deleted file mode 100644 index 748bf8f2a868956c21477276ddbb2a7f8dfe8bd7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8058 zcmWkz2UHVF7siMFv?m}%R7yYy5Rk-z(n3%mLJ~-*BGOa{i1c1U6H&TAC_$=*5&>U$ zMv)SVpaKC2O~n|hbO<37sRDoAo^yBh%-xwickj-(-*+e30&Q|q@SGqI56?-2sj(He z-TTk@|HnNivF_+|o8v)-2pfLx5ykJ8#KQx)gfPBg6JE4ZjLVe#R@~M0SYB8aCOY`- z*eQumHeaf3A4Iu7b`L`k6TFjdqS`$iOA(*kutxWl3JKJPbFOk?_e~weu6o%w-Lk4{ zf30*|`n9)M#18-El*^gduVc?#|6!o)F=sXrQv^HMFW%4_o43Ad`|(T5SYOLVn z@$i_BNSbt`RD#(dGqk7g>;_FTbpPOUNTL`(Ko%Dp-MnffkDdJ#7CClwPo#J@IM(Gr zvVY>cANt&-$%*1^5w$4vnj(_2PV-$dzn1sj`qnNi;m)OhaR00t-M#l^7AFWW0_%!m zGrB)luD~b$ClB-*U79?x`twoi4NQ`hch%E-ouqYPRW#!>;Wrix~>Y95T@MY z3^9~)OtcQQt12-I`jEYFJ*P$ZtHS5uSFRLXG=a#PvNCB8X>hA8Si-dI^LUT>`Ayj2 z-)Zv2wdZajKlG?jTZ;L`L71@)bL}Y1G$V(ZLA0n7IfppjqKZ>TF%Od}d=5|lw%`Zf z+g*OW8fFs-t7BoUT3*lSBEv0-yq2NGrL||iZMS$$6_)(++%er!!!IXAsg$u84Wcl? zl9LrIcoiC`BmZK#1U^h%i!30$?2Vy27obkV+xz?n(@8lpq6G^n`7C#f@~E6<85>$j z-qKe*ZwH*O0EDwF@VKWKC6mfjd@1dR6kYWf$JWMIaV$AY7Tsi$u1NuJeAfb^0Onq= z;3We%L3;A=3mvE)MHhebI*~NyfxP zC-buiIyer!MXpIKTV9&VZ$Nb>^4S%rBvUpN>TXDVIisJvnBT(n++-Fu)JS_h)g;Iou+3hu(2c_AA(IE0Hl2LM3p||9jo;`IP>WK=^`3E}9rlUz%9S>n1{GJDx6PjFTHD$=_qM*S_RP=7 zzS}1~J0Rnl5Xr3J7-2ZP!b2<*IGVr+Si73i^=+{;7UdjhPh+gKSoIlf_5EL zQKw|DUOQ)l)CJQaP)Kmm^!DXIl)~2l>T%k!yL)Meb2+sXSbFskvfPHKQgt3$@dIWp zt6ItmG25WvJ=zuiEV=aTiqalm`KF#M%u}C^K2ih(&A~Jbrp;C|-7<)UHx!~(@#@?a zazQs!19z@1|C9B#z03&DNd$>^t10HtK%98^f03IkZK%QFhS za8#Gy&Not1C`gDHpx>w6PGE3@lR(u-BZ-zd94#7zLwQsgrA8!&>lZQ zyVM|J3NM|&#^y;?u)xwm@A2w}dHVQ_@R;!m2)@|aK6O?h~k_dqkV;kbKMK?WbNrwBju%ri{K5P2HDU*Vy2SHY{ zFF>9~|iIZYco&Dp8R`WN>uh!s_6MqY{Z|z z?^mvXOe=TaGlF9uzb5iRDZHT_g(=`0fhQ_hSa|)}Y^qqRYKu7*g4Y##$asqkRbxDE z$?3GOsVC$`e@T}X!V;8p1{^xAyV-_;JkhgI0>`!|$f0UDKlG64O`{*f5qYDkgmW-k z(7_4>+1hsNGSQlg*$MTh{_MZ)muG~@Nl>J&PO4fJ>ksxbnjv{UG14SJ|HdMzAh@!F zRMF(t?qsC@D=m{dj^F@6!(s%_ScIm_lwA` zcRHD-&e+#es1;_N$ZkCZdj3zvxE`iR_w+QJ#w3)4_LyxpTKQ_-hp5E{r(GPW+u zo2>1gxY4+!Nt{`8pUH+!+M#2KFZO?q=FuA$nC-KcDti>Ppr zr|@y-Ceo?ih;0d)Z{b`$M1Rzw$`_f(AuDO=zx5f=*k0+>*rqjh10<0WsEX^UU;}B%UGNb?h)yvpsv0#`ZVtF6W)FYO*sAcbVQ$R(N@e@T7!g zp6z(~x$9es9*4#1haaxE?7$L^-R>)!2%PX8ZNV(ZH~mOTojKL@>yKOdw|^e4 zT{`-q7nYfGaIY{Oj#OxzE-{f9g;a<;Axb*aEcJ7Rm8}f6VqINxMlX3*p<9oKcD|Ck z)KP06`(o87$=KKex!J6V`DMLzbyCAVs%)i~v=Ze8I=nUnJ|%9NZ$vOy;iRUQq6)dCK`-i*}{y~bpN=)~9YMb^9H?&SlP-q|1Aj2cKzvG~h5de&JCkB-_qDxrd%FnQo2)CvF=wv_jaIq>16&V6b zF3A~{0Iyk#7dr&LxXP&b5>Zob*C1Mo41CdsP_f-nzN@bDfv~4Rq-Hu^ad6PWZyo6LQ=Yx$Kyh@nqr)$O0E(+6n zedZ5l>RwVRzo+bctkPqg3ncqzvEcEffR8(9=pOMP{qK=FS?_~UHPamE^SSHoVzZL^5dHIy76<>Yg*C-&YvPQ(MCh*u)J~GYE4OV>IKFJ$am9EiQ;82ciqoiU zyinZ(hI5pPD5yN>Ki+(Q!!xR!{k)M82vErhlO}Gb$h0vjQ@^s&+YCzphl{B(Do`c4 zbJtJ$6@KXhpvM;XMQ_ux51!3_4Co3pAltai|UB(fT6TDNy|WA~-G@ zJ+b67;Tc~j*2GfoNR*GTEa1%}63jzDo^ofC{+ZNO=oOwJ;rQ`|_lp+-n&rT@|B?M3 zXBFBpz6zY|Fm#5lS23v@lIbJ>z|SaS^(-v8NtwD{{L3=|ydPGX@i}`w)ODqR6b%s6 z$rabfZ0S};JZKPgRIy>SU5RGJ#U{#AJqvi>EIWHS3X;VOSO}V=LbtDA@`7g-_)=eK zBk^ph7ugz`F6w3w7R^U@DvebXsiF`NL2;Z8aXi@Io5agujz$BW_>Jn%C|76|DLdiB zem-(_+V9D%;1%-FC~07bd`%JIFN>f^RfL`P8iA$a~g`JCQP@jvfU-^i!Rr> z#DO+?Xmas0f&dCrIs7!<4I{7$DYb_<($E?W z&-@rZLo#<;1z$_Nlswuqj9LAVyJL@EcB{a>!mDdzA&TL-%A*3?VN;F0ADf;+s}@Ek zaL37{(H9=tpR}?ZU-q(%I9FHo{vuuSCfo_7AMgK_u#f8>2?I6z6!4yGAyYLFYsWU| zxU=mcm0y_8O2O(AR@*D^1M{=H+SRq~2QX*x1;s-~Hk)C-Phn3I&w|dC zGS_!Ts6nw9h202}Uu|>xQvPL&<+~ppjFbS5pAHX&PbVbmJjMUaEczXwyN;v zOA^yh&}^GCq-!c}`B{!+$@#uVDXzgFPv7(Fm0en{U*8$Y$e8E< zD38FuuF@8Wx6WCymkpD4%$RyyXcCr7x6)Y+-irtkq)F1;i;vTQ*~)(u!xkTXy)wB5 zaM1mC(JQ5H0X$T6XDNS)efwRx^r|yxx=whiCo#}f%>;sSYc$r$711nc95(9xEmNecDLbPc^p z@NDV~TG4nacT`uZbwDo^$N8~dd*hY&p`nQI?!0@*xTI&5vvvmEzDMOHIzqZ}m><4l z&>G{$Ud`oPe_NiBbNlT4`7udbs}dwzQ%T50DMAbYirq6;(Pc_W+Ul`u#-}}|(3iiV ziYu3_LZ2Xdf_jRdf}0bn>L~bS%{Q-WQy>vF1EJ#ykGJxo@m`GV|1vTkIKQ?L6z(g0 z&rp&!JSowNBti}%c2O!evSqbrMx?=jh}_u7^_N}B6mPqVrf(+JBVUTWgV@TSbatIz z0r?`uodLaCS(4K?H@V`_KzAu*D>`O}w>d~L_n~Mh1@%iGrKLNek~pJlE&&5IJQHH* zSk%m_iCl}K!S0pd8p}UuG@`pZ{0D|K!WW`^4uq-hj&fw_5J=3pi=x>YikZRI-tR{=OJM%3N1UcE?N>F8_WV?=-Z07SNTQU8U z`m?>tdrudQ1`m#f$%Zi908Mud()7B?ORCwMbYnYzL5zkTJYEYP{E!v`cD&ebx_NTg zL62p{y^;Ha^i*#`XKm8qd^>I=iQ;OrdG5SGJ<>voBE3{|a>DaIX$lz%j4b);X^}+E zR&F8emjxA{(7HF?*+c0HXmE6n4Zb?(BmI;K0EI0**mq?Xbvxhk2XDS<#*Z_1L&fZd zpD6E2^)IeR^Ye_B>+pPGx*r|BGw8&f{|YQU*VPp5U?c7~Q1&(yr!V@D;iJoWQwA#+zP-~>i^MnU~_uY{gx1)^Q@CQ}V1)j_ZvfT(haYJJyPEJKLqB;@WAmNz1l4H^1 ze6_DiI>5gC*9t^rhV~6{ah9>Qi*v0}Zp*GTd zS777XFO%sEWhjwjsthIV*fiirpNazcF3b%^r%m{vb}LaOPdCLT5@*a8J~?!~T~62z zFyC#OuKuL;*#keH%Q^b}d%SWNZMhGbCxfQH3LZ=4_`1S72sg(?$kBY^v zmt$iG8#H)2Buc)}O*~efEjo)wr7Z(9db1N$p;gbx!}_SL{JD4jgdHoTe$<9$wczT* zH-CQ!wo0ve>h$wrld2BnYdRE!x4R(#Is5(Xh@&7tAVb%b-0)!Z^k9dcj`_DI9H%Q` zDaatZUKgVZG=AAsa(r=jp|u}ha|IH@aM2#P8j|a1QowNrLr-BdOkEkPKkeVE!$bY- z@{`JegVxLC>#aKM)J~uFqU~oN{r(ji2Au1750DMG1)utOQ%Wy= zWJIJ$@HZ%CyrIZ?OJ{Y(fS0WT7{59h=J+eM4kQTpAN;=56e=ovPCAT2Y6rH4r#g~f zX5^Gr&v-8ULaA<4VMm5XkE^XczNlJBh0+UM4&~;tQ^s32{4;B$XA%+$492ta6C51h@J=d5_3RS%l0Q}0KmF?urzTbIo*88>l4 zKEoMcNohvoP&2<%)T)rXYEr6iL07JIiTdyG9v~Te`r8g3ib(4)&3ql{dYPW@9n-an z{j}eY+!8q;r-x%S|2`bpz}<=2=?d18^WF(=ZHqw>yHX0ic>2W*O)h9M= z@#4mwiBZZ9cff&})(=(xJ*|IoWPKh% zp1!lAo9ujZT}fuhx;#RX;e&UKXO$C4?FwyGlL$uv7pec`q!AE`rTHpSeHy_xg15+e zzr!bP1@dSItafV0XeA&6<0>QOagK67^PvJbVxg5%tYrJ}q}cd;wRW{JDF>QL=?F^l zEm*Pz5;7O=8)Jy67>!2ZI?l^fw&A)Gd?=_gB8w$~kb`SYVuVI>%xpRmFEokJLWUB; zEGZr|v^{x0&uOzq+YZnB;EznRAMKby(7I;=Ob%nz>DC~R9;Lp)><#?e@}65g4LJ>A zb^dxXMnC6YPiMuBEvf3rLkFXWauNv2jSyJm%39?oap$^kHIl zG@z4*VPw9m8i~MnK=ebk4`=;<64t(9n6bB;Q|kSn%^1!JVlY%>=}8@(-|h)qx;rcdVDFkPVSe5| z3?j)1-!Kl$_8mOAYTE2ZH||bUuYs^joWat-+OQ6Erp13**oQEX6+>p z&r#~#k6qQlS_+_OhD4Y~Q}SeGg`Rv_6eN2A;V4*}K%agFUzHzI2_vGAL2;JBpM#cH zd`E>Fvc@#qxkNqzqI(@eO&*A_ppA0@{(q4CDez!4rp`83=3mHcjbQQT(cTygw}$jG zfT$Jmx35tN%h7P&(soO^Qb*n~xTn%lVzlJSX}=FDB67?77x(gUjMZZU8O)mVwt?3t z5gIZ!xy`H{$EjswsT7Jnt=Qpvmfu@76N$JM;DxfrIiH0yPLMsx;K9(iNlm}y2!xmJ7-!hSk3tUC0iEB=~3S9 zYq$^ixfU?)3j{uqtHF3sg$!WWiSwX1?{wjz=h{+AQ2nTcFFc6GQVx$ zg!-)G3;b9jr)dUpOXO!JZe|lQ4V3ZewJupfqeP> z&NYE^cknI{mOL|0plK#{_(s^Hm}lz;PqJESH|*Rw6Tac?0kd7yuZzyNI7(Q2w3Er< zZw{p8BdbGwT4Z!2ywv^&KbgNeI1H|eARi|P+k#wd{o$5pU6ktS+jH!z_%Wq}4cOn+ zrox|w#*&U=p0L3(64pMDG#R|wZc!pt-LEjUdf49vua3tyJLeH*aY3FbzXJfX65`tkQ;b>CLvJ4hd zb;F>3D+`c^FIB7jkiZxbS-;@(a;3~XRt|04rhWPECoG$EZF!zSoi%+Ix)Xf?hx2%R%NwG#DfnKGM(~naqvkn~E*5XXP>e7|gbWg?&J%J0DC?ooo1r~x|9#5B z+MTo4s$l!a55+CC8y0uxoLZjzH9;F!9n>lGdy@b$u^w{lBC))Q)+I#bqf}vB&RCi< ulFR{8e;AWwO7}}{2AL;Wd!KSX1T2?5L{Qm)1P62@c diff --git a/tests/Pinta.Effects.Tests/EffectsTest.cs b/tests/Pinta.Effects.Tests/EffectsTest.cs index 1a8747528..d90705f55 100644 --- a/tests/Pinta.Effects.Tests/EffectsTest.cs +++ b/tests/Pinta.Effects.Tests/EffectsTest.cs @@ -477,14 +477,6 @@ public void Voronoi3 () Utilities.TestEffect (effect, "voronoi3.png"); } - [Test] - public void Voronoi4 () - { - var effect = new VoronoiDiagramEffect (Utilities.CreateMockServices ()); - effect.Data.ColorSorting = VoronoiDiagramEffect.ColorSorting.HorizontalBGR; - Utilities.TestEffect (effect, "voronoi4.png"); - } - [Test] public void ZoomBlur1 () { From e34c9b1b5819591122ffe63404afd65d9ed2018d Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Sat, 20 Jan 2024 14:14:47 +0100 Subject: [PATCH 30/31] Changed comments for translators --- Pinta.Effects/Effects/VoronoiDiagramEffect.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs index 47e44faed..e9cc45ca1 100644 --- a/Pinta.Effects/Effects/VoronoiDiagramEffect.cs +++ b/Pinta.Effects/Effects/VoronoiDiagramEffect.cs @@ -227,25 +227,25 @@ public enum DistanceMetric public enum ColorSorting { - [Caption ("Random Color Sorting")] Random, + [Caption ("Random")] Random, - // Translators: Horizontal color sorting with B as the leading term - [Caption ("Horizontal (B)")] HorizontalB, + // Translators: Horizontal color sorting with blue (B) as the leading term + [Caption ("Horizontal blue (B)")] HorizontalB, - // Translators: Horizontal color sorting with G as the leading term - [Caption ("Horizontal (G)")] HorizontalG, + // Translators: Horizontal color sorting with green (G) as the leading term + [Caption ("Horizontal green (G)")] HorizontalG, - // Translators: Horizontal color sorting with R as the leading term - [Caption ("Horizontal (R)")] HorizontalR, + // Translators: Horizontal color sorting with red (R) as the leading term + [Caption ("Horizontal red (R)")] HorizontalR, - // Translators: Vertical color sorting with B as the leading term - [Caption ("Vertical (B)")] VerticalB, + // Translators: Vertical color sorting with blue (B) as the leading term + [Caption ("Vertical blue (B)")] VerticalB, - // Translators: Vertical color sorting with G as the leading term - [Caption ("Vertical (G)")] VerticalG, + // Translators: Vertical color sorting with green (G) as the leading term + [Caption ("Vertical green (G)")] VerticalG, - // Translators: Vertical color sorting with R as the leading term - [Caption ("Vertical (R)")] VerticalR, + // Translators: Vertical color sorting with red (R) as the leading term + [Caption ("Vertical red (R)")] VerticalR, } } From 9b44f4e1a4a3cd2f7f4bcd792952bb1f4ddaf84a Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Sat, 20 Jan 2024 22:38:35 +0100 Subject: [PATCH 31/31] Added two more tests --- tests/Pinta.Effects.Tests/Assets/voronoi4.png | Bin 0 -> 8041 bytes tests/Pinta.Effects.Tests/Assets/voronoi5.png | Bin 0 -> 8123 bytes tests/Pinta.Effects.Tests/EffectsTest.cs | 16 ++++++++++++++++ 3 files changed, 16 insertions(+) create mode 100644 tests/Pinta.Effects.Tests/Assets/voronoi4.png create mode 100644 tests/Pinta.Effects.Tests/Assets/voronoi5.png diff --git a/tests/Pinta.Effects.Tests/Assets/voronoi4.png b/tests/Pinta.Effects.Tests/Assets/voronoi4.png new file mode 100644 index 0000000000000000000000000000000000000000..a004103a301ff6020f40d477760620a9877a9519 GIT binary patch literal 8041 zcmW+*2{csi``3@MRAfsVqOr@QWH-t_CX}%xV-zN-Y%}()h^#dl%M=-rrD+*85{4lX zhAd-=iLoz(F~+`||9t=Voaesxyyx7{d7k&a_jx{_=S{w8Yk6Ggj1UhG&vCGonLT&h z`OgH7aPLXXJ4W1r4`l*&5a3=>0$xcxJhJD(X4f6U-Y(`vro+wOBt8*G%AFR#o)8mK z8hd`?*m=>LeMxs!s&ec#ZrwgH?G#5sVlxTUpJ%$=;(ksPRvkW4`&GFzQ}XXszta-2 zVeDk1dxP`nF?Mx#btWzpOfq*zMR!hEg+Jagc*f#Pzc*$CKY<8Oh5PT% zjV+rVkXN3IW_95Yj+<&!f5%2-e3{F}nZIqLa#GS(M5U<- z=mzac3~i|HqnEen|IhR^dwGlh_jKgVrqSQZ9L+kf%{TMczA>3k_5qPKZ#K!IueqkX z{g2l~w(ZitOeP$3KZChwb|+_w;=Wa|5K&R(opGKC=tqWWa7fc5rPPLVEn<`b`0id)o_+=pa3# z{5v5T4mw#dOF^J@(0K~s%6@Dz@96Hyx!t@G_`Wk8rbd*rpRjB6OOhD4)ML&rOe5wIVxz#pF~{2jXSQ482; zv6VToVZ~@3P)KpEdO@tY>AVG~GLRrH;5%Q?A+fpv%mB{s>7|DRlq zFAbg`1`_Oi_K7*{{lk3oW99V_TM)AIUw2~nZJ5`SF3H+Q^Y6u_-(`?@nYKas^yE(w zUQuBI#o^fi16ljCLDGGdUMtBle_g2;-;lakpULE-%?U=SomfT+_(IEgsq^Abl89it z`=L#RuO+W>>&EZbL7`cj5b7E2O_^Z~w^;%UlU zuG{`|`0VnVquS3wX%=$$G&FpzeU|#f-o3wf!Ex(^4?JEB1h#u7L9=`t?HX}C^}!>O z8iuW9l^)H<^EP_OqDk4%GRNEJBXeh~HU&^?fFPpKU9mUjWZpADW0`LDG`RWa#>59r z)1sGYi7%+0;80l~LT{f}rQD0jML~tgmchj4OxrRyj><^56EueCcZ}!m^wk=En(_3S zYpa%y3Vk6x54Za}_16G!I6U&T+DB_}kaYBXhvghxg(RQJwq_cdP=vb$MgdgG7PujP zB{b?N@YWjIms#Jlr{%+DRKd;Ir^Acps6AnHiVr-HT}Ls)TE(yc=8KvA4MGaWDdS}9Vzci3S? zUN_!7F#oN{tIN7jIyi_&cduzrXIsVf$Jvcj-Va*NrS7m-931sO9;5I_ z*RQkOw0Si+*?i^JLd!8LVIGM4ZQD8#j2ZtisPtWMh#+2=$1Y8k3$776s5XmWqG z$gRmG#X;D5kWp}*O?5Isyee~3rK(R14vt~I?$8bSGxO6D`e)J3=x3csC*N%Pxw{g1 z>iv?!#G0arBnhlyBpd?~Kj^l5`Kus*N)P=%o|i^Z0In5PitUe&Qg=*AcNJ6D|Ce+5 z+{uC0>P4?^mOz0YzMXzQaB*O1{wPW~6gP}2ArJWEw#dK_I7F{y_LVCtttHzHvPF?1 z#1!F66ye)~?#@Ai7&T>tjwJp54HjhU;~GINGON%0W;@)02&Kp@6>BMOo@xh8*H&Z^ zA((Rv;G~Mzrkvi>SR@V-hFuwUnm4>JTb^){rgzsa=&5G^Ax~Pn8CJ3S_Ixx~76-+A+%Y_fIc3rm;%91rA)#G*|l*h-al*?bSR-1wV9I4=dV+)zvsK8B?`F%6?AoeDd~ zz}#azx&@(IU|moVFoRy~Rhi}XmLX(6ewCIhUp?{Jzw0g?RLYhE42+)y*@X-QR)1l5 z*gcf<9o+X7BKTl-$Fes4J!l$7J5%7Kj+!Vr;DBY-of*t+#} zf$ksu!C9$k{jtejig*DDqbB2iD~w}fR;8OPdqK(=w~tf$!wS&?g@$+qg(zL+4Ejlp zhH?gXuX0h;Za|Gbnss5C+4sKMgrsO~DukaL%A8FfKJsJkYMW++TZ&j+GNPu;0(M%F z!cwh%G3MC&NTbgt%#XTIq7NxdOh0!Xhk-b(wC(DI&$U7_&V9hL?R(B81*O3)&eZRg zJ{L@zuLG1BILU@Di|lj(NE1gpkZ?iHC#R~Jeo>fVk%|fZ;>frhfUXe&@27=k>Brc8PnCvu)k~hN zoEkvVVt84FQ4W7PF1?Qk;dvyeDwj``W&X+{|HUB0XYA8GnhCu zuw5M(xkRl zg_pA~?e!xEepLxY_Ml@L7^zoTqZWJDsVy5y18rw3@3ozl_Or}BaUDYkp2N)E%hEarNis ztgV~gly~0*#HR^-4SP=6n}#`C z{VfraZCLi0|6vNwo=h9eT*27a3cL)$s)wJh5YIE&$ebbK)oOwo%5PFM+*S&;`2Uy5 z`iA1V_8i3L)aK>RzNU22aE#*s5sK28FS-YaP#w?hvaLU48}sd~QoXKnloQ)rZA2*W zL1$Iwie{;5k?HKaYkj|~@i4LTrcLzqCpDh~L zu}MuDk<#Oy>9wjIhaglNsGD_;(sK{BD%(Fq-sw4+XH&u7Y}d5lXeq2j*ZX9m4jB2< zbZ(?c3thcxt5Rhv?&aLr0tn9|N(V?76-OA}EDe+S)*dtdoqTEGCtSUHf22Fnl-($f(J<~mS+FW_v8uCB(trH>zC{8k?H&Uu8|Gy& zqNeWrdO2%GE+IRl;xssJ!3slGC8yp-?vh^{&GI%`PD`cUsu&}x9hW%MpmxOFPGQ$a zsb^#(n3___p4w~h)!;+`g=;=X$jXVqG&i$+xXHmPO#Ds;!*LSjn!OhpK5@$`W^hkt z*7x;02*EUuMVd;Pc3=9vQZ~EPqRmx|Qmp|BwGf){35AGbJ_iXIy6$73+OG?*<R( zu-Bp6DEP~lG(wB?#&j*Pn3-TRc%_$-V&_QzFKSb=O^&xq&?A4=pj+IS1S0ZQ3;oEb z=N5y7W~t>BBe~(J*Z1vBVJ|BKoV{{`#ke&K#}0+P=eHkCALaZZ7TAoEZl|&?bl{6^ z@hr4?<;KK5nzH5E63|&KcHnw;^wv(Ku@Um-UKz7GdT}vz`>0n-AB;lwqeNsc?!+TM z+rF7`VdaW)MoB8S6-bYAD#?4<|0|ucPm=49bo9X?#^-G+pn3acV`_LCD?CdC>)_wk zm&2E44uqhR?WL|?+$|ChIZv0->FKSVOmwSyd>^Qh>r;RC`Q;8316HOwjEw)gx5RB_w}}4U{~YXo{_cYnMw|`ldf|bfNUgyf==V*Wh`2)NX%BsTGwQFfuafBHSj0 zKMG9!Jmx|q0_h4Pj!Bt)r$0HT5V*NAXBK{T3*!wI5}!BWegqQyKl?Ad-NacZgG( z*DR_F-{^CD3+O_2pvsPmx?k3C+d8Gs!t@wwQ=)^0ZDzYyl^1@Zd7e^H2AUCmN+os^ z)k~o_8e6Pg_(5RYDZL24*>wI)rro6*Sg`s+m!8|%0p{Rtt<{fLrSxWtS8qq%{- zN@R+Bjl9>YU(&GNo@r5|cgsAqYhL!b(W&{TVjjwiBviZb7GztsdNt`*m_lVI*lrz7 z_j_~yKqo1S61s7(BXDuPR&B+ejFibM&+IbEThT_&il(xG_20ecy0X~UYKrVmeNJ0_ z#S+41mSPMiwoPt?fmp@du$L!w$0SA+sM!6#dp&{# z&$Vgj&M!m*-@zPZC`K<2QKQ=b1(Z`_7m?xHNf!38t&@F7i9LuPXj$>~+Rj|&2%zNkc)Bl!^Um>-~6*D4UO2s1gExn}j>F2VJTnGTg2Zj*Y$` zl>(0s2tW;^R=ekZIQthZG`Z_We-&J}Yxf%dDssvH-mvj z!ufiI7E_!|=uQBN8Kx$)35BRkMjvsh@&Wr#-C^+uQiyLU!dtKCr3|io{f<#9YC8bF zSG;XpX&vE8mCDzl&W8sVWV%U3GeE<|-tH5V@4>V0c{%Kk$utGKzS->6{N{_Z#?m{N z(hBCAq`)KfXk0ANbx_=`A&>bM&yv@*Eabfmr7KWZyP+dm)X@FrQ+tf7Po#r~yq-9B zZ7}w$-TLog)<(>n1BC1@8kombng>0k1ly7#^6wNb?YtP54pAZ2e=4o=@lo69j>gtI zlSj4nnvylOt$@8iW1{O@CTtja(EHQlF4}YB>`EAJ(8X4kljBT_MdGWw+jk7-o(;?9 zK{ldi%7Sf!=i(~gU{99)+#6^f|Dn`jE4{7|DbTRmngJTz4iR(i{jIn?Z5fgX;^V1B z)O79*Fx~TeTUK!b6<*9CmrDQChZ8TV7$qIoMdI&-p{F!EjVS`KEv z4Z7dibj!BkstemNh-OzCZ24n9AbW8bqxQID#3SR}rPr1sa5Pm1 zw$vWi`(4_IoziYn*1M~Tmhss0b8V}1TQMxcZpi`sO_fIUanVXq>JV?wM(1RAY&sb3 z)L2$mS&!c-eb4YyVV(7caI33*Td9`LFNd8|rTrguqxT-&4%p0W%I`JnaBPf$A>Y74!4@;JUfEc4xF@1mOccl z4`I_;4Q9@O@ky_x$^0uZFU=_B0TkhQNl+Cd1rT*bZ(;cruRHKdwvGYDnTTo=UOUPu z|6(xNX-ED|lFd8s9DDIXpU*eJGo2B&d%^8(buGAYC*AB9?Mu=X!m?qh-q+iQMMQ9i zuvz<6Ll)nd(9bsDE!_`dH7PeI27KxQ+u(fIt%-{()z3lqo`0GOXG|pCh&)`ujK~>? zwcb-cW<95Yeh<7--%_mt^YPcuvQVhOPlXSr$Y$w*w?a&*f6DWKp;>t*D~3VK?M3LG zQ*(`_(`B`P=A$H@gn2%hoUPVK5!Ae|Q=qer zG*~HmJ^%cD)@P$#PU=W(%fVV8F5~)#aP}{wur5xwMnjfruuOHb%=YRb`5cFD8K<1Ro{Si8lY&S0ZB>H(}wB#~9aWVqkuR$+gfrxUFfQ#D9ZvexK=9q;o zu@Q6RM+dYGl83Q1h@WgdqF*=Nm0>6{+L)LF&(%*pV1CudNHxeAndj8B`d!!t%`W+p zja9931M930NO5ZRjYH$|de`z-929DNi7l_Egf<8U6?-L^g>&D1eKBN30%k#Z{7~ea zDZ1S=`<#Fl`Tm9a)G__xxsiCsz)Xy{Ahovr7ogu0d+vl58vd909s^U#THOxdGAqmq z%m>bGsaOf0(3#ZTTIi2C=yqsKyb&~T|1FS`+A2O`Nr3|lil++BB0a>6MBFjFD&wXQ z;Z~J}T+D?2WyN2I1G|4z@b6kv^R#=#4-F4-x;G7O@kh|z98>$2u(WnebTpx~Eas(`!H*)d zGH_lzhcRw&PqN{(qyMh(5=HErKuph)Y~#g5S+SiF z_Y;4s*2Z3l>2ujyy$#r;tcuS&qpf!apvYRqRU{Bmv9v!lPDprUBKzFx zM%#vJCfmFx@o_5!uOrS>rlI%SrhZ!XM6EQhU~5A!KehQ4v#GR1$L)vd6Bf@JbZ=Mh zer98vU*Q57I}lvfxOGqcHLD%Ju0GZA+aGnbf)Er?%?A3#eMkd zuE=y5eDh)RU$xX4+SA$+*y+%VF!2{mSd+XS&)b!sZ0l>K^UvO~-R2+>3RPYo`OsN_ zNYsa&^QY`d0}0l9@)X$-gfnKb1|zxmZ(%{TvW~De#4#g@}1dORx&uzg(=gp*2 zh(_&MXK_lSC2H1NW_l>QVYfO4zyEFj{oyapAteB%qDLO?4AWE3lbLT3HGNw7kscgQ zc^K6<9Z)sZ>+H+O8C0^}%4BXUQx1)L20@Ej=|@P48{-MV2f2af@yu2T1mzZBE|Cm( ztV1bQEp8b~oSR&g_plBj5RHydVy(=8Cicm2i{7?SX`kXfuA_017ygA_&8t>o!Ikw+ z+?pfURs235I+T91Eg!i$)}b3r+Z<{uf9q_WgB2tsSp=DX#w5a3W_h!ia`6KST||$j zGiir?=QOUb=*OuQM$G}qGfVIkrc*8IZwJVV2kR;gRR%#i0-H1&06Uyg9w zhlb@#kR{M`^^X@y#)ne=S=2G2%^ z4XEG93mDRT>AB_S=}r@c$n1r#h_jTC!V~m?t=f-XA_|e>d;@qj1MHYDK!_SWhJwK> zqx^Hm^by5|TyD~sH7^06-czhbJNOg-(>^WARWBWA8H|%RTXEQVUoluIcWZoC!6ll} zey4UCfhsT3R5Hk4;&e_6(eP&iukjV4#3Jh6ng&pykIl~o7Do=a%40C(ibANB#=W+S zAvvnmT6a|R5f~+!IKm1XOfB4nkK))6*PdaEkhW#L^ROUc-R5a+d`$Dfp3=L3fP;+1 znuw!^3o^)#g(&S&dqIB-*B+Qs5%X~&6jx{+ix!T=kY#)`VWQR{-;!WF!4ZD>*m!Q6g74=9n-F95Y9h z*o2VQa-W-wIp%7P|N6dO@9*<^J>T!=d7jVb`+eS@<4L}5XDxi}^f5j@K4FNBr30`1 z^B00gdG{pNZ4+K|IQTNeNsxC%33?^*@hJpBEG?YE3YT!zI=IBa#)@y^wDQQgy--h5B3G^8aCq4P=v z%Q9|RSSCFF9B&2b@H&52?0@TB1+6t~lF!mQ7k%)jaNnt=ssPLFTNrKb-`oE~i|$)m z72)q{K7Kc1FF;{K>7sNjN~E-yyb#G2O^;7w?~ZuVt|)qUe>h~*U4u6n7Q_VC~$w}MvzqwCYnAqUl*c=ewfh` z@gJ#?9Y9^aOCSX3SqKwd5;X-0Gm%8=A=vkT+p^5Eb|-fQLd{@#O%a5s%mikDc?4 z(9IIeqWV;4GEYvjzh!VB%-3x@V|w*EwMcEa$A+NnfKu!(iWi=GI=S!@8lp)#)DtcD7OjmYnM-LpU2OSb8gKHM)dJic9KvXYJAj}u3n#F)sWWQ$;=61C`+KmlV>x&Mk; zuE?VJ(8f5T6X^=_qQ|^+`Z-g?(Vqx zbB_^mAKRZj5%N6KAU;NmG?j#D8-CM{bbmb*ph14ClODA7gFt)XwrzL@65OLU-fcF%U%3&MrjzdAOz*1cJ@|EG3TcDEMIwchvX@P%e*AWHe9jVb+c3SN zapABe0n;+~Xbg*L$ecHHwheKNl5jr;^0NP6RkcEHXz<($*>_L_JfKHI*Htj_X6I0c z0Vm|1xLKcg6m_KOy>?`}+1H{CzalmFl|`4vE-_gCcOzQcqd=A3>u(y!5-r`bErJSW z=Oe*;7&(7pp^*>-PoAC@LinfAb{8MsoM)1(1XvVzmG{v(W9WRc1i}**CR3snXdBLa zi?dWtuK3oc?@SFAWLL?8pVo}r1KI#yRTehj){xmbH_s%9nLwqdG&)0G{x)Crs=1pl zm18Ufn5r;~KD=|@VDj3ALka&!Y2^dVcIeZ6@{ZYyi*?Z$lcXY%V++xl@OEJ2!oe!q zUnO>YsI^Vu7iV};cFXM$>A&?gnZ#MX2eh{y5sn_CZRJ^WKk?#nHuf9(fQj~CtMo-D zm(O@EF6*A2d>iBal3wD_|Ij7D$@2L79bC*j%>P03o@qVDc`!I53DBK^NMX8gOf)yx z)hbQelu|DU`{`_8+~RfW@-@_vx)p+VKL$Pp4e=Nj7tLAdP+Iz3Af)XJ3jJ~;=f#PfgYN7z8isAoQ?${e+_rF0E0mb^j22Ocv0Qqagw2kWS%8||G3~xSrb~kwe+-w~|6_(lzu3 z&~WUU9uRxGBNpA${bj~r3(#H|x#0KSmg3*)z8pNg{_*8xLHP>Yz`7|-KHO|h&=5L z-6;V!FFGKgYNoItz89!arDl-6WYhP+-SE2#r|6_X**kWq$EL%Qsziq5Slz5)Hc`r! zV~pwYys~|^eZ4#)_dAkDS1Jb1RgpPNL)FEla0ditmJdN3!N^Y9ZeI{`zxrFh5rgC3 zv=rEOkW(*WAxmIc@QODqD@(9)xU57+fnaONy7|KT{OdO?SD=wqRjt~e>_om-55;?v zQx_7zhW_a=G$n52+3lc@##09K6SCQmkiKwI+VEVR$GB?@4K8MtJIQ&W(`w`QFa5=p zAY5}#5X{st+j6biy{;j7u3wz{PLBJVjir76WB3Qmmi?$2HLwxlq2dxi*`GdXJ=3Y2 zcF!P07SiD;w472x9DxRZ?2v7#M%h0rEfgTac7P&slpuGRysxybnx&Vp`GbfD#gq(a zAS-rlEOhLLF+$B=+W$=WOOjzV(-pHAAXuzI?#F1Yb;mGUDObDdHH<1|`QF{X6FIXw zapHX_d;2W?p;lAe?QMN}wW8rSO>*T=$WPHC7zZLl_S23T6maW7x^<{g+{?W#I!hL#)h_8; zqSct-LUh!fxaK7lyK$OU!N3z8!>D+jEHrvGnc#SJTQhg5{&fcKb`T(YrBK6PrpD4GK0Y)t z{zB1(&e~U^`#Yw4$ybML2b{JXP*$Sj!sSHAGLm+CY2LAZC6bVPDxq7fHN~6!_A2jt zd4sh`y%P3U93UPl!>*PUzMmzPRGJZX1-ji(4gWJ?<9J=iH9;`2_eqLMM7AOsLG-$0 z8?2#GbQQBwuhL)ey9K+%Zc1V1hHPEhF14hz{#I*_tmH=&>OB-)_;Pw7&U(PRfmC4% ze$nvMo1173Q4q`%#j>o1MiV9aJ2qua?W-D$OW&*z<{9V?v@%E-5h70EH&$ttjE@(| zbNFE4k{}Ukd$Y>tUQC9`oy+d@r;{Pw!^GnY@~>3O^i7bJ?)SsB1r^jgX5FT-2Bael z_bdIM+>GYlyykzl$n4n^X@5aOeTXvCnf@O#UV@>fG5lhA(RA~fJfEmC*DveSkziFB>ZG zTi;(Ouk5nqZhmR+{DA*-I$cEG*V=6)Cb3j#`)c~oy}q92<8P8@4dFy7?cV5Ygp_d# zn5bZCFu@pfRPqeB9f;j_v+J)}{-?mT37aGP3Outh*euwZvx>yL9?Jjp?RR&uS?A$xOu7Aj>vRXN@jkk)LT&H~ zzR9|&n)kR*hs7yR&iP}LKn8qoJSP6cK7{Rmwq)1@<8`{Wz-?HJ_^M2azx0m{yGC!|%6<8%d(|-&J+KkA#WV-@YeCe`0v9Xs7s(=N3voL~d^&=SRWnp?mPR z4O)~#cYeOd*?N4p$t#9gU}bIS=RRqZ(~+igkT!q0M~<6mou&CI=}#Ra3L5R!jlW-L zS`QhmPI?jk*Jvk<>cyW%mGfmMH|Fzs;)4m?x@hkyYB!`rg>H;cz-& zR>Gt#qVBxL93CGgD~oUTq6~H8m=Z)6rQT?Eb48|02yg4W#vqzzAxOaY4~z0Ps&BOp zwy|kQ<9+5js<`mM)4$VhXAY0O%d%1$P(aX)eAD0RZ#`eITMQ1}mp8ASOi{3vo5i#E zp2u$)^d5S8uMjB&#{1T+pN=@a^;~*b#ei?rG|J)4Ebl$-P0thu_1jb!)>z~RdHKoB zqJ0us+Np5TYQ=TaxPz-0elwr{S&5V_S_3J6zx?^6VJj6-RzViQFgo=I7Mo}1BN;8e zDP1>o>D4L*cV44Jkf7ra+R;&1gb+JxHk=yrYIf~5vxCcDF==QTG9!5qMYG{kCd>DS zk1yRx(!ep1z$lyGt$^$W*X=wO0u#FaR;}FdG$dxN+(5KxIp<3~PmUs=1D|@z3RgO5 z0vZOE?{LTEmLmjj24qeL+F`F$B&WDNLu zbobsU`}n~duXZ$?=Uu}xb+9Ut14OwyHXLi0%`YY5Ut@ke`<7ljwtJ>!i(p19zWAau zt4mkjenPJ@gVkz7c&AN9#;GYmHcK*6_j|>OA{rX65y>^j2^XVBRQMJamJ22g^}H%h z!Mo~Msz|^&k8}{^K40T>>fXiTf3NJSP|%pcF&~N~j}uNdUlU&*U%GgRuR>wt&FXpI zp_!jsz4p6NoRZzmFtS4St!(2GpOcbp_ZcSXvxd+RskjDh+4w;5Z9ieToH0&FzbY>X z;G~uQ%HQ{eg$?NlbjaO1CT_c%37#V-Tij@u9BQ!b7_J?QE*z|XyW~s6+J+2?;UXZt ziScS&DVTjQHV;Z@jQN+0rjMKo@eiTb*DbmS$#y6-G;Lh`wEr&3|95{tUwds*IVsI+ z>R}dB!aGvdUv}(e{%>z!=o1hLP`Ba&O2yvWt!n4Holqe<`sJuJV5%p3i7%6BTDCJ} zA1L1SP~RQfQBXTMNa0RoMNi3=vWQJy4o%M~qzpubl6cgdpX;M}BNx z?T`Yd&&oFbT8?0Ez7$@tRWF$5d%_!zq%A)>OwrugkO?;v)~%T}C<}z?Q;HVcDt+P1hS*QZCwR!#LO9FtbhjAp>o|KIkV;un|R($HGgHDZCts) zt{J;j&wc-z^Cj6&x!0g<{u;UfZ{%I&m_k^8Q6sAveImuO;z36su&GZjeY1CObj(He z>mK}kvY%FE1Do07-oZVLo#hJ{z(?D7iF_Uk{MXHo{=yPmF#mcNB+}VrSTXCcvH-YX zHQ21qs)@6#yizq%dacR@iF}dTTWD8&W4`u8BFTPXU*nfMHH6>Ju%mcgjmGc6Yr%HM)oUKl&_ac!iR*2uVy3 zY_rU%Urd&7Gp#=+8ogRs5HOhgW7-C`@!dtahNm1i0kLJc?i%JnDflhMmjie&3&UaqJk&+W4PDreFnt&=37>TzD7=o-=eh#OP7H=Cf26CXjkqi*|;kh`aO?z z{B6Qcu-A#cLW%j}_s5R#nYh1i(o0L%qGRQ1J39lQH}HU{$GM+n>;v%R4sYs2n-6?O zE7{GxkQLpL-nQrUoCn&cfDY#hTBr*GURBY0deQKgD+bYZlUf16J@Xe*P}|HrUal+O z*18|6mnbfJd45FRM?)bYBDv3LSwBiNW{F+yV`>dih_BHb07NbS`Es>Mi&|!k9}TpT zH0AAPP<-1;!+y??x6!hr(VE`nbq_bc%dcI=%ULPL8vWX#ve505K4gE*Q#o)om8ihA zp2brp!^hh?n=<6?+lEU8b-kYDFKct%T0bDv-0H5?utrb9#%N=B7UpLlcJGorfP$`4 zj2Zye|M!=~7-bv*UWO69>13g&ynO!(j>R)_hYgp3u_HRheg73>D?3e5a>CCs3d%K zs)51Reackxz5Og?TiU+(8;2px_2DzunCdalne-W(SVpK784sX+h1h3?vsBV&K3D(A zC~|{VEiyZ-kJy(at$2&$O0QMqM*lJzYjZqkjFDM*@5-wKbo)7S&cyL^F#H#+1RlSp z<#{;bfGw5GQkUoF|B#xmQwgbrD@rC@M(GHXI>P)IyY_q_0mzu1W2Xg}91hZB6%VK{ zMaNZ%7xNtP?w$6=FIWv$3IBdg1MFloKz*tQdQc%3yuw4$)f_u%x&p#P(|Jw3e6SX~ zd_x}={#&f*azNT3La8zy>{#z}zG069$43_aRC_ZuxR@B^;lpfDd`_do%AHN(bFxm|l!RBlaD5AZj&=sKl*sm%h&lmPjs&$BffMfXU>A%P9q(jzNx%Sz}H?eolj1 z%{%MLI6!>U4WGd$*zY&MP)Vbe!0b%;>enOUR1uYVbuFne*y`IIpL zu^Fkie9uV4hu)`6>L`<87tESJnbVc(z#`&CqFw#Zj+hn(*^HmG4YOOVdu%3W{u*wmD3X>M?y zh81D0U&i*Te{K7+pP=QfIJW3#G?s-iDKpR>1-431Y6p!|Fk?DXj~?Y7$^$)k4~&9q zD5x;3AP3)?8*I|ltky1MOO79X6Pova__YYWx!B1fNRBesoWq+(Ww$9U#5b(oY-BZ6 ztiG+>QI{g=Ff>LrnUNsWV5%>8G<_?+lv@QQD8KdeaYk8Xm?*xO3qA3fwp?B#h!ub$NZ5k3+(Hke1 z8vh8oYNLDhR0yjs=QV<2ZompZio8Pk@lXqs_Ql8TZQv0_@3RL7^xjiM-DwY{%59n z;{(xOc2w%I!d73ZElOc-0_CUnWA|VN&y8iE&N^E{TtEU7Wr{G6qVi4A|s4L3wy?7K-Rb_uYu3A;$O0er)5I04B>A~$dL z*e1cKZr2J(a~(3(?k>tAVivDwZx9%BrZ9jgcG)=D;s6fUa`Xo3_96WRA1ROg)u!4A8_U--lwCGfVK6B70uV*##fOhpxNlwS~@H z4pNK;^&^NCy|idB4gEO*R{4~72tVE@pDEmOV~f>jL9 zR;s>w`{Bp2`d5(?f4T^=59SK%PvV%MN>~Rhd-O=tx0N`sbv`>7K zvD8$AdlrTXQJ=Cmyeseb@M2rtNdd5I%-L0TR3_Z>3TLA$e@wUhKaSFXvJF<2DFzQ5 zZ*MK}Djr;P;(Ob{RVAUKpy8P%0{6cqh+x(;LqY7IA`r+h+Gd^oL2h`hWgS38M9M|C zC!TvaXG!}cnvA3ueHNCgyOa!0$YAPTrtE+B1{=vkHj-gGhY}N~f6?Few}i|n(7T3a zro7!w_cm_rMksKFYBP$Mfk;Azj=c-Z#LV=4R3?rrZH;3>;Gr;5N)4;P!m-5P(C_-p z`$r4x@QTIqh>ANk!|SI!Ijx=_-_5pRe`Le{{XtD0@9s+g^TLC=TI-5~=r1xBcNhF% z5x~Z2iiq$R4Ury9G^bU9Y@*ujE=L>}4C);~FB~#lEyci;HMciD5!nMvj#d0?j3ta- zZ=QMPyu?4cGt}}HRQr<$WnqK6#%WD>@(Us&WtOjf?N|BCQQcNzBLkNP$C&LXg*FJE zZt=ebRJmmFOKddV2hhbssL6Avc~f(4E@#lO(7|tl8Y(A+kIMaO#@)FUwZdO>>$I>i zW>7ZiY)#>-Q^y-Vuy~dtkTBZBV zppQ}Gea?Q|JeI?MI3^kwTYym|r|ty1(iijQ9|sI*(7r#Nod0^;;G{?6&>jKO*&kzi ziVvY-pWs9}^hByI;QGzAlJ($(>f;D?>ZZB}ys)L}t_(0r5l1TMqDr-Vb#TLBsro>^MS!amBxl0a1QOolLnsqZ%)kX}yHdn36x~*egxS~X#xq;!A6h~ktoz=R>UwqIh zCWrfG%|2Eu@yTHq#ufDSBihfK*_XpA`)@8Q(bf zMjShBSc0XYICwpUS%M7tl<>!Q?1RpQe@xfzY$cv!5*41TGQ-}KkgC6r+SO8MVJaoxKPW?FhBDd_i;}>_@*n$UC-KZ-8q*nARXZ9fY#LjECZI*|K)V}lsy6xd~ zj7tb&S>M!X^*UkZdb3iVAVDx${wav$l|4E5`oZ@bf&?4b=2;T@X4|M5mgUpxb8S9& zaQp<1%;GK6fp#cJWs$0WXjOqqQ(7hO{BBX5p4u&EJ??eMbL8!u8uyvD$payqXJGd*xAur*&jL@zG1XN`FPv(cCJk@Uqb1zq(gCY{O89 zQ2TIz&)?A)kW5UK!bQgT-6lG!Mk0>XT#(?^lc)JaEE%6SA`B8I*^!x?fSb3X{LFYs zjfDe|b`y1sfvu{&$$Rx%%K=>) rF$muhN8axSE~S6z0@m|^4+H>7{ap@D%C~r@@q7>~J4?dlJ8}O9jOJ$D literal 0 HcmV?d00001 diff --git a/tests/Pinta.Effects.Tests/EffectsTest.cs b/tests/Pinta.Effects.Tests/EffectsTest.cs index d90705f55..43bebceb4 100644 --- a/tests/Pinta.Effects.Tests/EffectsTest.cs +++ b/tests/Pinta.Effects.Tests/EffectsTest.cs @@ -477,6 +477,22 @@ public void Voronoi3 () Utilities.TestEffect (effect, "voronoi3.png"); } + [Test] + public void Voronoi4 () + { + var effect = new VoronoiDiagramEffect (Utilities.CreateMockServices ()); + effect.Data.ColorSorting = VoronoiDiagramEffect.ColorSorting.HorizontalB; + Utilities.TestEffect (effect, "voronoi4.png"); + } + + [Test] + public void Voronoi5 () + { + var effect = new VoronoiDiagramEffect (Utilities.CreateMockServices ()); + effect.Data.ColorSorting = VoronoiDiagramEffect.ColorSorting.VerticalB; + Utilities.TestEffect (effect, "voronoi5.png"); + } + [Test] public void ZoomBlur1 () {