From 65caa860c821771ecdd153bd648c55497b41abe8 Mon Sep 17 00:00:00 2001 From: Lehonti Ramos <17771375+Lehonti@users.noreply.github.com> Date: Sat, 30 Nov 2024 03:47:56 +0100 Subject: [PATCH] Struct-valued enumerator for pixel offsets --- Pinta.Core/Algorithms/ColorDifference.cs | 2 +- Pinta.Core/Algorithms/Utility.cs | 21 ----- .../Enumeration/PixelOffsetEnumeration.cs | 86 +++++++++++++++++++ Pinta.Core/Extensions/Tiling.cs | 12 +++ Pinta.Effects/Effects/AddNoiseEffect.cs | 2 +- Pinta.Effects/Effects/BulgeEffect.cs | 2 +- Pinta.Effects/Effects/DentsEffect.cs | 2 +- Pinta.Effects/Effects/DitheringEffect.cs | 2 +- Pinta.Effects/Effects/FragmentEffect.cs | 2 +- Pinta.Effects/Effects/JuliaFractalEffect.cs | 2 +- .../Effects/MandelbrotFractalEffect.cs | 2 +- Pinta.Effects/Effects/MotionBlurEffect.cs | 2 +- Pinta.Effects/Effects/OilPaintingEffect.cs | 2 +- Pinta.Effects/Effects/PencilSketchEffect.cs | 2 +- Pinta.Effects/Effects/PolarInversionEffect.cs | 2 +- Pinta.Effects/Effects/RadialBlurEffect.cs | 2 +- Pinta.Effects/Effects/SoftenPortraitEffect.cs | 2 +- Pinta.Effects/Effects/TileEffect.cs | 2 +- Pinta.Effects/Effects/TwistEffect.cs | 2 +- Pinta.Effects/Effects/VignetteEffect.cs | 2 +- Pinta.Effects/Effects/ZoomBlurEffect.cs | 2 +- 21 files changed, 116 insertions(+), 39 deletions(-) create mode 100644 Pinta.Core/Enumeration/PixelOffsetEnumeration.cs create mode 100644 Pinta.Core/Extensions/Tiling.cs diff --git a/Pinta.Core/Algorithms/ColorDifference.cs b/Pinta.Core/Algorithms/ColorDifference.cs index e3fc6b908..255890896 100644 --- a/Pinta.Core/Algorithms/ColorDifference.cs +++ b/Pinta.Core/Algorithms/ColorDifference.cs @@ -35,7 +35,7 @@ public static void RenderColorDifferenceEffect ( foreach (RectangleI rect in rois) { - foreach (var pixel in Utility.GeneratePixelOffsets (rect, source.GetSize ())) { + foreach (var pixel in Tiling.GeneratePixelOffsets (rect, source.GetSize ())) { destinationData[pixel.memoryOffset] = GetFinalPixelColor ( weights, diff --git a/Pinta.Core/Algorithms/Utility.cs b/Pinta.Core/Algorithms/Utility.cs index 4f5efa265..b580f77bd 100644 --- a/Pinta.Core/Algorithms/Utility.cs +++ b/Pinta.Core/Algorithms/Utility.cs @@ -38,27 +38,6 @@ public static double Magnitude (this PointI point) public static double Distance (this PointD origin, in PointD dest) => Magnitude (origin - dest); - /// - /// 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) - { - 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: rowOffset + x); - } - } - public static PointD Lerp ( PointD from, PointD to, diff --git a/Pinta.Core/Enumeration/PixelOffsetEnumeration.cs b/Pinta.Core/Enumeration/PixelOffsetEnumeration.cs new file mode 100644 index 000000000..337741b06 --- /dev/null +++ b/Pinta.Core/Enumeration/PixelOffsetEnumeration.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Pinta.Core; + +public readonly struct PixelOffsetEnumerable : IEnumerable +{ + private readonly RectangleI roi_bounds; + private readonly Size canvas_size; + internal PixelOffsetEnumerable (in RectangleI roiBounds, in Size canvasSize) + { + if (roiBounds.Left < 0 || roiBounds.Right >= canvasSize.Width || roiBounds.Top < 0 || roiBounds.Bottom >= canvasSize.Height) + throw new ArgumentException ($"Rectangle is out of size bounds"); + roi_bounds = roiBounds; + canvas_size = canvasSize; + } + + public PixelOffsetEnumerator GetEnumerator () + => new (roi_bounds, canvas_size); + + IEnumerator IEnumerable.GetEnumerator () => GetEnumerator (); + IEnumerator IEnumerable.GetEnumerator () => GetEnumerator (); +} + +public struct PixelOffsetEnumerator : IEnumerator +{ + private readonly Size canvas_size; + private readonly int left; + private readonly int right; + private readonly int top; + private readonly int bottom; + private int x; + private int y; + private int row_offset; + private bool has_more_rows; + internal PixelOffsetEnumerator (in RectangleI roiBounds, in Size canvasSize) + { + int l = roiBounds.Left; + int r = roiBounds.Right; + int t = roiBounds.Top; + int b = roiBounds.Bottom; + + // --- Read-only + canvas_size = canvasSize; + left = l; + right = r; + top = t; + bottom = b; + + // --- Mutable + x = l - 1; // Initialize to just before the first x + y = t; + row_offset = t * canvasSize.Width; + has_more_rows = t <= b; + } + + public readonly PixelOffset Current => new ( + coordinates: new PointI (x, y), + memoryOffset: row_offset + x); + + public bool MoveNext () + { + if (!has_more_rows) return false; + x++; + if (x <= right) return true; + x = left; + y++; + row_offset += canvas_size.Width; + if (y <= bottom) return true; + has_more_rows = false; + return false; + } + + public void Reset () + { + x = left - 1; // Initialize to just before the first x + y = top; + row_offset = top * canvas_size.Width; + has_more_rows = top <= bottom; + } + + public void Dispose () { } + + readonly object IEnumerator.Current => Current; +} diff --git a/Pinta.Core/Extensions/Tiling.cs b/Pinta.Core/Extensions/Tiling.cs new file mode 100644 index 000000000..9bc38a897 --- /dev/null +++ b/Pinta.Core/Extensions/Tiling.cs @@ -0,0 +1,12 @@ +namespace Pinta.Core; + +public static class Tiling +{ + /// + /// 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 PixelOffsetEnumerable GeneratePixelOffsets (this RectangleI bounds, Size canvasSize) + => new (bounds, canvasSize); +} diff --git a/Pinta.Effects/Effects/AddNoiseEffect.cs b/Pinta.Effects/Effects/AddNoiseEffect.cs index 196f9c4c6..7d2babf45 100644 --- a/Pinta.Effects/Effects/AddNoiseEffect.cs +++ b/Pinta.Effects/Effects/AddNoiseEffect.cs @@ -129,7 +129,7 @@ protected override void Render ( // being used to render the effect, but will change if the effect is tiled differently. Random rand = new (settings.seed.GetValueForRegion (rect)); - foreach (var pixel in Utility.GeneratePixelOffsets (rect, settings.size)) + foreach (var pixel in Tiling.GeneratePixelOffsets (rect, settings.size)) destinationData[pixel.memoryOffset] = GetFinalPixelColor ( settings, rand, diff --git a/Pinta.Effects/Effects/BulgeEffect.cs b/Pinta.Effects/Effects/BulgeEffect.cs index b9e4f5eb0..bec4f3686 100644 --- a/Pinta.Effects/Effects/BulgeEffect.cs +++ b/Pinta.Effects/Effects/BulgeEffect.cs @@ -76,7 +76,7 @@ public override void Render ( foreach (RectangleI rect in rois) { - foreach (var pixel in Utility.GeneratePixelOffsets (rect, new Size (settings.sourceWidth, settings.sourceHeight))) { + foreach (var pixel in Tiling.GeneratePixelOffsets (rect, new Size (settings.sourceWidth, settings.sourceHeight))) { destinationData[pixel.memoryOffset] = GetFinalPixelColor ( settings, diff --git a/Pinta.Effects/Effects/DentsEffect.cs b/Pinta.Effects/Effects/DentsEffect.cs index 1df517a71..aeabfa3b6 100644 --- a/Pinta.Effects/Effects/DentsEffect.cs +++ b/Pinta.Effects/Effects/DentsEffect.cs @@ -82,7 +82,7 @@ public override void Render (ImageSurface src, ImageSurface dst, ReadOnlySpan dst_data = dst.GetPixelData (); ReadOnlySpan src_data = src.GetReadOnlyPixelData (); foreach (RectangleI rect in rois) { - foreach (var pixel in Utility.GeneratePixelOffsets (rect, src.GetSize ())) { + foreach (var pixel in Tiling.GeneratePixelOffsets (rect, src.GetSize ())) { dst_data[pixel.memoryOffset] = Warp.GetPixelColor ( settings, InverseTransform, diff --git a/Pinta.Effects/Effects/DitheringEffect.cs b/Pinta.Effects/Effects/DitheringEffect.cs index e6677c0aa..519ce9aa0 100644 --- a/Pinta.Effects/Effects/DitheringEffect.cs +++ b/Pinta.Effects/Effects/DitheringEffect.cs @@ -56,7 +56,7 @@ protected override void Render (ImageSurface src, ImageSurface dest, RectangleI } } - foreach (var pixel in Utility.GeneratePixelOffsets (roi, dest.GetSize ())) { + foreach (var pixel in Tiling.GeneratePixelOffsets (roi, dest.GetSize ())) { ColorBgra originalPixel = dst_data[pixel.memoryOffset]; ColorBgra closestColor = FindClosestPaletteColor (settings.palette, originalPixel); diff --git a/Pinta.Effects/Effects/FragmentEffect.cs b/Pinta.Effects/Effects/FragmentEffect.cs index caac82bea..9a309a99f 100644 --- a/Pinta.Effects/Effects/FragmentEffect.cs +++ b/Pinta.Effects/Effects/FragmentEffect.cs @@ -76,7 +76,7 @@ protected override void Render ( FragmentSettings settings = CreateSettings (source); ReadOnlySpan sourceData = source.GetReadOnlyPixelData (); Span dst_data = destination.GetPixelData (); - foreach (var pixel in Utility.GeneratePixelOffsets (roi, settings.sourceSize)) + foreach (var pixel in Tiling.GeneratePixelOffsets (roi, settings.sourceSize)) dst_data[pixel.memoryOffset] = GetFinalPixelColor ( settings, source, diff --git a/Pinta.Effects/Effects/JuliaFractalEffect.cs b/Pinta.Effects/Effects/JuliaFractalEffect.cs index 1d505fea2..5be3ad19f 100644 --- a/Pinta.Effects/Effects/JuliaFractalEffect.cs +++ b/Pinta.Effects/Effects/JuliaFractalEffect.cs @@ -75,7 +75,7 @@ public override void Render (ImageSurface src, ImageSurface dst, ReadOnlySpan dst_data = dst.GetPixelData (); foreach (RectangleI rect in rois) - foreach (var pixel in Utility.GeneratePixelOffsets (rect, settings.canvasSize)) + foreach (var pixel in Tiling.GeneratePixelOffsets (rect, settings.canvasSize)) dst_data[pixel.memoryOffset] = GetPixelColor (settings, pixel.coordinates); } diff --git a/Pinta.Effects/Effects/MandelbrotFractalEffect.cs b/Pinta.Effects/Effects/MandelbrotFractalEffect.cs index 77fc9107b..2d011c379 100644 --- a/Pinta.Effects/Effects/MandelbrotFractalEffect.cs +++ b/Pinta.Effects/Effects/MandelbrotFractalEffect.cs @@ -132,7 +132,7 @@ public override void Render (ImageSurface src, ImageSurface dst, ReadOnlySpan dst_data = dst.GetPixelData (); foreach (RectangleI rect in rois) - foreach (var pixel in Utility.GeneratePixelOffsets (rect, settings.canvasSize)) + foreach (var pixel in Tiling.GeneratePixelOffsets (rect, settings.canvasSize)) dst_data[pixel.memoryOffset] = GetPixelColor (settings, pixel.coordinates); if (settings.invertColors) diff --git a/Pinta.Effects/Effects/MotionBlurEffect.cs b/Pinta.Effects/Effects/MotionBlurEffect.cs index 50881bdd3..0d0f61c0c 100644 --- a/Pinta.Effects/Effects/MotionBlurEffect.cs +++ b/Pinta.Effects/Effects/MotionBlurEffect.cs @@ -99,7 +99,7 @@ public override void Render ( Span dst_data = dst.GetPixelData (); foreach (var rect in rois) { - foreach (var pixel in Utility.GeneratePixelOffsets (rect, settings.canvasSize)) { + foreach (var pixel in Tiling.GeneratePixelOffsets (rect, settings.canvasSize)) { int sampleCount = 0; for (int j = 0; j < settings.points.Length; ++j) { PointD pt = new (settings.points[j].X + pixel.coordinates.X, settings.points[j].Y + pixel.coordinates.Y); diff --git a/Pinta.Effects/Effects/OilPaintingEffect.cs b/Pinta.Effects/Effects/OilPaintingEffect.cs index 0ea45cf80..c0efd8de1 100644 --- a/Pinta.Effects/Effects/OilPaintingEffect.cs +++ b/Pinta.Effects/Effects/OilPaintingEffect.cs @@ -54,7 +54,7 @@ protected override void Render ( OilPaintingSettings settings = CreateSettings (source); ReadOnlySpan src_data = source.GetReadOnlyPixelData (); Span dst_data = destination.GetPixelData (); - foreach (var pixel in Utility.GeneratePixelOffsets (roi, settings.canvasSize)) + foreach (var pixel in Tiling.GeneratePixelOffsets (roi, settings.canvasSize)) dst_data[pixel.memoryOffset] = GetFinalColor ( settings, src_data, diff --git a/Pinta.Effects/Effects/PencilSketchEffect.cs b/Pinta.Effects/Effects/PencilSketchEffect.cs index c672c83fc..d70958d8f 100644 --- a/Pinta.Effects/Effects/PencilSketchEffect.cs +++ b/Pinta.Effects/Effects/PencilSketchEffect.cs @@ -72,7 +72,7 @@ public override void Render (ImageSurface src, ImageSurface dest, ReadOnlySpan dst_data = dst.GetPixelData (); ReadOnlySpan src_data = src.GetReadOnlyPixelData (); foreach (RectangleI rect in rois) { - foreach (var pixel in Utility.GeneratePixelOffsets (rect, src.GetSize ())) { + foreach (var pixel in Tiling.GeneratePixelOffsets (rect, src.GetSize ())) { dst_data[pixel.memoryOffset] = Warp.GetPixelColor ( settings, InverseTransform, diff --git a/Pinta.Effects/Effects/RadialBlurEffect.cs b/Pinta.Effects/Effects/RadialBlurEffect.cs index 1df748f12..44f0132ce 100644 --- a/Pinta.Effects/Effects/RadialBlurEffect.cs +++ b/Pinta.Effects/Effects/RadialBlurEffect.cs @@ -86,7 +86,7 @@ public override void Render (ImageSurface source, ImageSurface destination, Read Span destinationData = destination.GetPixelData (); foreach (RectangleI rect in rois) - foreach (var pixel in Utility.GeneratePixelOffsets (rect, settings.canvasSize)) + foreach (var pixel in Tiling.GeneratePixelOffsets (rect, settings.canvasSize)) destinationData[pixel.memoryOffset] = GetFinalPixelColor ( settings, sourceData, diff --git a/Pinta.Effects/Effects/SoftenPortraitEffect.cs b/Pinta.Effects/Effects/SoftenPortraitEffect.cs index 8aee43911..cc7a8888b 100644 --- a/Pinta.Effects/Effects/SoftenPortraitEffect.cs +++ b/Pinta.Effects/Effects/SoftenPortraitEffect.cs @@ -100,7 +100,7 @@ public override void Render (ImageSurface src, ImageSurface dest, ReadOnlySpan dst_data = dest.GetPixelData (); foreach (var roi in rois) { - foreach (var pixel in Utility.GeneratePixelOffsets (roi, src.GetSize ())) { + foreach (var pixel in Tiling.GeneratePixelOffsets (roi, src.GetSize ())) { ColorBgra srcGrey = desaturate_op.Apply (src_data[pixel.memoryOffset]); srcGrey.R = Utility.ClampToByte ((int) (srcGrey.R * settings.redAdjust)); srcGrey.B = Utility.ClampToByte ((int) (srcGrey.B * settings.blueAdjust)); diff --git a/Pinta.Effects/Effects/TileEffect.cs b/Pinta.Effects/Effects/TileEffect.cs index 7c3ac4ff0..50ecd0e72 100644 --- a/Pinta.Effects/Effects/TileEffect.cs +++ b/Pinta.Effects/Effects/TileEffect.cs @@ -125,7 +125,7 @@ protected override void Render ( TileSettings settings = CreateSettings (source); ReadOnlySpan sourceData = source.GetReadOnlyPixelData (); Span destinationData = destination.GetPixelData (); - foreach (var pixel in Utility.GeneratePixelOffsets (roi, source.GetSize ())) + foreach (var pixel in Tiling.GeneratePixelOffsets (roi, source.GetSize ())) destinationData[pixel.memoryOffset] = GetFinalPixelColor ( source, settings, diff --git a/Pinta.Effects/Effects/TwistEffect.cs b/Pinta.Effects/Effects/TwistEffect.cs index 77c776209..cebfc0a69 100644 --- a/Pinta.Effects/Effects/TwistEffect.cs +++ b/Pinta.Effects/Effects/TwistEffect.cs @@ -51,7 +51,7 @@ protected override void Render ( TwistSettings settings = CreateSettings (); ReadOnlySpan sourceData = source.GetReadOnlyPixelData (); Span destinationData = destination.GetPixelData (); - foreach (var pixel in Utility.GeneratePixelOffsets (roi, source.GetSize ())) + foreach (var pixel in Tiling.GeneratePixelOffsets (roi, source.GetSize ())) destinationData[pixel.memoryOffset] = GetFinalPixelColor ( settings, source, diff --git a/Pinta.Effects/Effects/VignetteEffect.cs b/Pinta.Effects/Effects/VignetteEffect.cs index da47d18ca..5bffac71a 100644 --- a/Pinta.Effects/Effects/VignetteEffect.cs +++ b/Pinta.Effects/Effects/VignetteEffect.cs @@ -98,7 +98,7 @@ protected override void Render ( VignetteSettings settings = CreateSettings (source); ReadOnlySpan sourceData = source.GetReadOnlyPixelData (); Span destinationData = destination.GetPixelData (); - foreach (var pixel in Utility.GeneratePixelOffsets (roi, settings.canvasSize)) + foreach (var pixel in Tiling.GeneratePixelOffsets (roi, settings.canvasSize)) destinationData[pixel.memoryOffset] = GetFinalPixelColor (settings, sourceData, pixel); } diff --git a/Pinta.Effects/Effects/ZoomBlurEffect.cs b/Pinta.Effects/Effects/ZoomBlurEffect.cs index 7719ce567..a2ab03a16 100644 --- a/Pinta.Effects/Effects/ZoomBlurEffect.cs +++ b/Pinta.Effects/Effects/ZoomBlurEffect.cs @@ -76,7 +76,7 @@ protected override void Render ( ZoomBlurSettings settings = CreateSettings (source); ReadOnlySpan sourceData = source.GetReadOnlyPixelData (); Span destinationData = destination.GetPixelData (); - foreach (var pixel in Utility.GeneratePixelOffsets (roi, settings.size)) + foreach (var pixel in Tiling.GeneratePixelOffsets (roi, settings.size)) destinationData[pixel.memoryOffset] = GetFinalPixelColor ( source, settings,