From ad9766c45dbe909afe02a8466b3e1f6a1f244ac6 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 30 Jun 2016 14:04:06 +1000 Subject: [PATCH 01/11] cleanup Former-commit-id: a680527d251607d8925867ed9826bbad23564609 Former-commit-id: 06d34a2e01dc6a1ca0d757621699ee3bb473201b Former-commit-id: 455a73c3426b690087a4eba9c81b8fe920e22280 --- src/ImageProcessorCore/PixelAccessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageProcessorCore/PixelAccessor.cs b/src/ImageProcessorCore/PixelAccessor.cs index 23749769b..47a179731 100644 --- a/src/ImageProcessorCore/PixelAccessor.cs +++ b/src/ImageProcessorCore/PixelAccessor.cs @@ -113,7 +113,7 @@ namespace ImageProcessorCore throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height."); } #endif - return *((Color*)(this.pixelsBase + ((y * this.Width) + x) * 4)); + return *((Color*)(this.pixelsBase + (((y * this.Width) + x) * 4))); } set From 3085f50ea219e778058d04a55bf12a4f4e4a5aac Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 1 Jul 2016 10:44:57 +1000 Subject: [PATCH 02/11] Add a few comments [skip ci] Former-commit-id: c62a04c3030c1322782ccd7ec9823f1ccb2629a2 Former-commit-id: 097737d5586667952794d078b6d65eb198f53c48 Former-commit-id: 26b4e34da805f42ddd0bec44127020756f7140dc --- .../Samplers/Resamplers/CatmullRomResampler.cs | 4 +++- .../Samplers/Resamplers/HermiteResampler.cs | 3 ++- .../Samplers/Resamplers/Lanczos3Resampler.cs | 1 + .../Samplers/Resamplers/Lanczos5Resampler.cs | 1 + .../Samplers/Resamplers/Lanczos8Resampler.cs | 1 + 5 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ImageProcessorCore/Samplers/Resamplers/CatmullRomResampler.cs b/src/ImageProcessorCore/Samplers/Resamplers/CatmullRomResampler.cs index 03af3272f..0b5031df8 100644 --- a/src/ImageProcessorCore/Samplers/Resamplers/CatmullRomResampler.cs +++ b/src/ImageProcessorCore/Samplers/Resamplers/CatmullRomResampler.cs @@ -6,7 +6,9 @@ namespace ImageProcessorCore { /// - /// The function implements the Catmull-Rom algorithm. + /// The Catmull-Rom filter is a well known standard Cubic Filter often used as a interpolation function. + /// This filter produces a reasonably sharp edge, but without a the pronounced gradient change on large + /// scale image enlargements that a 'Lagrange' filter can produce. /// /// public class CatmullRomResampler : IResampler diff --git a/src/ImageProcessorCore/Samplers/Resamplers/HermiteResampler.cs b/src/ImageProcessorCore/Samplers/Resamplers/HermiteResampler.cs index 6c1540a19..49193a3de 100644 --- a/src/ImageProcessorCore/Samplers/Resamplers/HermiteResampler.cs +++ b/src/ImageProcessorCore/Samplers/Resamplers/HermiteResampler.cs @@ -6,7 +6,8 @@ namespace ImageProcessorCore.Processors { /// - /// The function implements the hermite algorithm. + /// The Hermite filter is type of smoothed triangular interpolation Filter, + /// This filter rounds off strong edges while preserving flat 'color levels' in the original image. /// /// public class HermiteResampler : IResampler diff --git a/src/ImageProcessorCore/Samplers/Resamplers/Lanczos3Resampler.cs b/src/ImageProcessorCore/Samplers/Resamplers/Lanczos3Resampler.cs index 9bc842f61..a78b6c066 100644 --- a/src/ImageProcessorCore/Samplers/Resamplers/Lanczos3Resampler.cs +++ b/src/ImageProcessorCore/Samplers/Resamplers/Lanczos3Resampler.cs @@ -8,6 +8,7 @@ namespace ImageProcessorCore /// /// The function implements the Lanczos kernel algorithm as described on /// Wikipedia + /// with a radius of 3 pixels. /// public class Lanczos3Resampler : IResampler { diff --git a/src/ImageProcessorCore/Samplers/Resamplers/Lanczos5Resampler.cs b/src/ImageProcessorCore/Samplers/Resamplers/Lanczos5Resampler.cs index 67b704fc4..05af2dd7f 100644 --- a/src/ImageProcessorCore/Samplers/Resamplers/Lanczos5Resampler.cs +++ b/src/ImageProcessorCore/Samplers/Resamplers/Lanczos5Resampler.cs @@ -8,6 +8,7 @@ namespace ImageProcessorCore /// /// The function implements the Lanczos kernel algorithm as described on /// Wikipedia + /// with a radius of 5 pixels. /// public class Lanczos5Resampler : IResampler { diff --git a/src/ImageProcessorCore/Samplers/Resamplers/Lanczos8Resampler.cs b/src/ImageProcessorCore/Samplers/Resamplers/Lanczos8Resampler.cs index 28a305c65..8c9a9237d 100644 --- a/src/ImageProcessorCore/Samplers/Resamplers/Lanczos8Resampler.cs +++ b/src/ImageProcessorCore/Samplers/Resamplers/Lanczos8Resampler.cs @@ -8,6 +8,7 @@ namespace ImageProcessorCore /// /// The function implements the Lanczos kernel algorithm as described on /// Wikipedia + /// with a radius of 8 pixels. /// public class Lanczos8Resampler : IResampler { From 59a57b7716993cc5be7258449b30214db76978c8 Mon Sep 17 00:00:00 2001 From: Sverre Rekvin Date: Fri, 1 Jul 2016 22:59:20 +0200 Subject: [PATCH 03/11] maybe working Former-commit-id: 696365d1b984a55ecb1063cb21e0a2f48b454274 Former-commit-id: 11caf1a2b1f7c67874c8ac1fbf33c53318068c8e Former-commit-id: 4cb2557cbbf7e5d6eba652eac57d969a1d2705fd --- .../Samplers/Processors/RotateProcessor.cs | 46 ++++++------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs index 33c31bf9c..7822eb7f0 100644 --- a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs @@ -16,7 +16,6 @@ namespace ImageProcessorCore /// /// The image used for storing the first pass pixels. /// - private Image firstPass; /// /// The angle of rotation in degrees. @@ -38,16 +37,6 @@ namespace ImageProcessorCore set { - if (value > 360) - { - value -= 360; - } - - if (value < 0) - { - value += 360; - } - this.angle = value; } } @@ -81,41 +70,37 @@ namespace ImageProcessorCore // Get the padded bounds and resize the image. Rectangle bounds = ResizeHelper.CalculateTargetLocationAndBounds(source, options); - this.firstPass = new Image(rectangle.Width, rectangle.Height); target.SetPixels(rectangle.Width, rectangle.Height, new float[rectangle.Width * rectangle.Height * 4]); - new ResizeProcessor(new NearestNeighborResampler()).Apply(this.firstPass, source, rectangle.Width, rectangle.Height, bounds, sourceRectangle); - } - else - { - // Just clone the pixels across. - this.firstPass = new Image(source.Width, source.Height); - this.firstPass.ClonePixels(source.Width, source.Height, source.Pixels); } } /// protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { - int height = this.firstPass.Height; - int startX = 0; - int endX = this.firstPass.Width; - Point centre = this.Center == Point.Empty ? Rectangle.Center(this.firstPass.Bounds) : this.Center; + + Point centre = Rectangle.Center(source.Bounds); Matrix3x2 rotation = Point.CreateRotation(centre, -this.angle); - // Since we are not working in parallel we use full height and width - // of the first pass image. + rotation = Point.CreateRotation(new Point(0,0), -this.angle); + + Matrix3x2 tran =Matrix3x2.CreateTranslation(-target.Width/2, -target.Height/2); + rotation = tran* rotation; + Matrix3x2 tran2 = Matrix3x2.CreateTranslation(source.Width / 2, source.Height / 2); + rotation = rotation*tran2; + + Parallel.For( 0, - height, + target.Height, y => { - for (int x = startX; x < endX; x++) + for (int x = 0; x < target.Width; x++) { // Rotate at the centre point Point rotated = Point.Rotate(new Point(x, y), rotation); - if (this.firstPass.Bounds.Contains(rotated.X, rotated.Y)) + if (source.Bounds.Contains(rotated.X, rotated.Y)) { - target[x, y] = this.firstPass[rotated.X, rotated.Y]; + target[x, y] = source[rotated.X, rotated.Y]; } } @@ -126,8 +111,7 @@ namespace ImageProcessorCore /// protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) { - // Cleanup. - this.firstPass.Dispose(); + } } } \ No newline at end of file From d6fd816ad9ea3ab70d7567734c0dcccbee2b90dc Mon Sep 17 00:00:00 2001 From: Sverre Rekvin Date: Fri, 1 Jul 2016 23:09:04 +0200 Subject: [PATCH 04/11] First working version Former-commit-id: 9a29900c60ed46679ec35b19764f2716a071cdcc Former-commit-id: 5d27440cde0a1b795322f33df43c1e23a5686158 Former-commit-id: 94ca246cfdf0d6ad960284d4dcd626632d15c300 --- .../Samplers/Processors/RotateProcessor.cs | 26 +++---------------- src/ImageProcessorCore/Samplers/Rotate.cs | 2 +- .../Processors/Samplers/SamplerTests.cs | 2 +- 3 files changed, 6 insertions(+), 24 deletions(-) diff --git a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs index 7822eb7f0..847c68ef4 100644 --- a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs @@ -41,10 +41,6 @@ namespace ImageProcessorCore } } - /// - /// Gets or sets the center point. - /// - public Point Center { get; set; } /// /// Gets or sets a value indicating whether to expand the canvas to fit the rotated image. @@ -54,22 +50,12 @@ namespace ImageProcessorCore /// protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { - // If we are expanding we need to pad the bounds of the source rectangle. - // We can use the resizer in nearest neighbor mode to do this fairly quickly. if (this.Expand) { // First find out how big the target rectangle should be. - Point centre = this.Center == Point.Empty ? Rectangle.Center(sourceRectangle) : this.Center; + Point centre = Rectangle.Center(sourceRectangle); Matrix3x2 rotation = Point.CreateRotation(centre, -this.angle); Rectangle rectangle = ImageMaths.GetBoundingRectangle(sourceRectangle, rotation); - ResizeOptions options = new ResizeOptions - { - Size = new Size(rectangle.Width, rectangle.Height), - Mode = ResizeMode.BoxPad - }; - - // Get the padded bounds and resize the image. - Rectangle bounds = ResizeHelper.CalculateTargetLocationAndBounds(source, options); target.SetPixels(rectangle.Width, rectangle.Height, new float[rectangle.Width * rectangle.Height * 4]); } } @@ -78,14 +64,10 @@ namespace ImageProcessorCore protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { - Point centre = Rectangle.Center(source.Bounds); - Matrix3x2 rotation = Point.CreateRotation(centre, -this.angle); - - rotation = Point.CreateRotation(new Point(0,0), -this.angle); - - Matrix3x2 tran =Matrix3x2.CreateTranslation(-target.Width/2, -target.Height/2); + Matrix3x2 rotation = Point.CreateRotation(new Point(0,0), -this.angle); + Matrix3x2 tran =Matrix3x2.CreateTranslation(-target.Width/2f, -target.Height/2f); rotation = tran* rotation; - Matrix3x2 tran2 = Matrix3x2.CreateTranslation(source.Width / 2, source.Height / 2); + Matrix3x2 tran2 = Matrix3x2.CreateTranslation(source.Width / 2f, source.Height / 2f); rotation = rotation*tran2; diff --git a/src/ImageProcessorCore/Samplers/Rotate.cs b/src/ImageProcessorCore/Samplers/Rotate.cs index 49441fbb5..09c49e588 100644 --- a/src/ImageProcessorCore/Samplers/Rotate.cs +++ b/src/ImageProcessorCore/Samplers/Rotate.cs @@ -33,7 +33,7 @@ namespace ImageProcessorCore /// The public static Image Rotate(this Image source, float degrees, Point center, bool expand, ProgressEventHandler progressHandler = null) { - RotateProcessor processor = new RotateProcessor { Angle = degrees, Center = center, Expand = expand }; + RotateProcessor processor = new RotateProcessor { Angle = degrees, Expand = expand }; processor.OnProgress += progressHandler; try diff --git a/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs b/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs index ac610746e..29ab3be26 100644 --- a/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs +++ b/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs @@ -471,7 +471,7 @@ using (Image image = new Image(stream)) using (FileStream output = File.OpenWrite($"TestOutput/Rotate/{filename}")) { - image.Rotate(63, this.ProgressUpdate) + image.Rotate(20, this.ProgressUpdate) .Save(output); } From 82a8c479ca7c4b7ecccda3d2bfc8718436225cf5 Mon Sep 17 00:00:00 2001 From: Sverre Rekvin Date: Sat, 2 Jul 2016 00:39:30 +0200 Subject: [PATCH 05/11] rotate Former-commit-id: b47614eda6c4bf9f50968bd324f27dcec5f685bb Former-commit-id: 8a1a1b13359ba260906d8439f131d0f07ce81cba Former-commit-id: 9ce6a4e24ec11eb2e0a90f9e12ea103c4dbacd8c --- .../Samplers/Processors/RotateProcessor.cs | 62 +++++-------------- 1 file changed, 14 insertions(+), 48 deletions(-) diff --git a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs index 33c31bf9c..0b6dd1a11 100644 --- a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs @@ -16,7 +16,6 @@ namespace ImageProcessorCore /// /// The image used for storing the first pass pixels. /// - private Image firstPass; /// /// The angle of rotation in degrees. @@ -38,24 +37,10 @@ namespace ImageProcessorCore set { - if (value > 360) - { - value -= 360; - } - - if (value < 0) - { - value += 360; - } - this.angle = value; } } - /// - /// Gets or sets the center point. - /// - public Point Center { get; set; } /// /// Gets or sets a value indicating whether to expand the canvas to fit the rotated image. @@ -65,57 +50,39 @@ namespace ImageProcessorCore /// protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { - // If we are expanding we need to pad the bounds of the source rectangle. - // We can use the resizer in nearest neighbor mode to do this fairly quickly. if (this.Expand) { // First find out how big the target rectangle should be. - Point centre = this.Center == Point.Empty ? Rectangle.Center(sourceRectangle) : this.Center; + Point centre = Rectangle.Center(sourceRectangle); Matrix3x2 rotation = Point.CreateRotation(centre, -this.angle); Rectangle rectangle = ImageMaths.GetBoundingRectangle(sourceRectangle, rotation); - ResizeOptions options = new ResizeOptions - { - Size = new Size(rectangle.Width, rectangle.Height), - Mode = ResizeMode.BoxPad - }; - - // Get the padded bounds and resize the image. - Rectangle bounds = ResizeHelper.CalculateTargetLocationAndBounds(source, options); - this.firstPass = new Image(rectangle.Width, rectangle.Height); target.SetPixels(rectangle.Width, rectangle.Height, new float[rectangle.Width * rectangle.Height * 4]); - new ResizeProcessor(new NearestNeighborResampler()).Apply(this.firstPass, source, rectangle.Width, rectangle.Height, bounds, sourceRectangle); - } - else - { - // Just clone the pixels across. - this.firstPass = new Image(source.Width, source.Height); - this.firstPass.ClonePixels(source.Width, source.Height, source.Pixels); } } /// protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { - int height = this.firstPass.Height; - int startX = 0; - int endX = this.firstPass.Width; - Point centre = this.Center == Point.Empty ? Rectangle.Center(this.firstPass.Bounds) : this.Center; - Matrix3x2 rotation = Point.CreateRotation(centre, -this.angle); - - // Since we are not working in parallel we use full height and width - // of the first pass image. + + Matrix3x2 rotation = Point.CreateRotation(new Point(0, 0), -this.angle); + Matrix3x2 tran = Matrix3x2.CreateTranslation(-target.Width / 2f, -target.Height / 2f); + rotation = tran * rotation; + Matrix3x2 tran2 = Matrix3x2.CreateTranslation(source.Width / 2f, source.Height / 2f); + rotation = rotation * tran2; + + Parallel.For( 0, - height, + target.Height, y => { - for (int x = startX; x < endX; x++) + for (int x = 0; x < target.Width; x++) { // Rotate at the centre point Point rotated = Point.Rotate(new Point(x, y), rotation); - if (this.firstPass.Bounds.Contains(rotated.X, rotated.Y)) + if (source.Bounds.Contains(rotated.X, rotated.Y)) { - target[x, y] = this.firstPass[rotated.X, rotated.Y]; + target[x, y] = source[rotated.X, rotated.Y]; } } @@ -126,8 +93,7 @@ namespace ImageProcessorCore /// protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) { - // Cleanup. - this.firstPass.Dispose(); + } } } \ No newline at end of file From 860f82ca4e729cfb0a44c8612db7e1be1a3ebfdf Mon Sep 17 00:00:00 2001 From: Sverre Rekvin Date: Sat, 2 Jul 2016 01:27:56 +0200 Subject: [PATCH 06/11] Rotate and skew, shorter code fewer if, better math Former-commit-id: 1c43328c71d549a566fe1c3a655f97c9ce9fad05 Former-commit-id: db8c7b880c3f17b85feb7277477eddc50f6dffb5 Former-commit-id: 405ff7e2dccdc33b5cbf53a07208a57528616c6b --- .../Processors/ProcessMatrixHelper.cs | 41 ++++++++++ .../Samplers/Processors/RotateProcessor.cs | 46 +++-------- .../Samplers/Processors/SkewProcessor.cs | 80 ++----------------- .../Processors/Samplers/SamplerTests.cs | 4 +- 4 files changed, 59 insertions(+), 112 deletions(-) create mode 100644 src/ImageProcessorCore/Samplers/Processors/ProcessMatrixHelper.cs diff --git a/src/ImageProcessorCore/Samplers/Processors/ProcessMatrixHelper.cs b/src/ImageProcessorCore/Samplers/Processors/ProcessMatrixHelper.cs new file mode 100644 index 000000000..428fc4084 --- /dev/null +++ b/src/ImageProcessorCore/Samplers/Processors/ProcessMatrixHelper.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Threading.Tasks; + +namespace ImageProcessorCore +{ + + public static class ProcessMatrixHelper + { + public static void CreateNewTarget(ImageBase target, Rectangle sourceRectangle, Matrix3x2 processMatrix) + { + Matrix3x2 sizeMatrix; + if (Matrix3x2.Invert(processMatrix, out sizeMatrix)) + { + Rectangle rectangle = ImageMaths.GetBoundingRectangle(sourceRectangle, sizeMatrix); + target.SetPixels(rectangle.Width, rectangle.Height, new float[rectangle.Width*rectangle.Height*4]); + } + } + public static Matrix3x2 Matrix3X2(ImageBase target, ImageBase source, Matrix3x2 processMatrix) + { + Matrix3x2 translationToTargetCenter = Matrix3x2.CreateTranslation(-target.Width / 2f, -target.Height / 2f); + Matrix3x2 translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width / 2f, source.Height / 2f); + Matrix3x2 apply = (translationToTargetCenter * processMatrix) * translateToSourceCenter; + return apply; + } + + public static void DrawHorizontalData(ImageBase target, ImageBase source, int y, Matrix3x2 apply) + { + for (int x = 0; x < target.Width; x++) + { + Point rotated = Point.Rotate(new Point(x, y), apply); + if (source.Bounds.Contains(rotated.X, rotated.Y)) + { + target[x, y] = source[rotated.X, rotated.Y]; + } + } + } + } +} diff --git a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs index 0b6dd1a11..7a8e5f981 100644 --- a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs @@ -13,12 +13,10 @@ namespace ImageProcessorCore /// public class RotateProcessor : ImageSampler { - /// - /// The image used for storing the first pass pixels. - /// + private Matrix3x2 processMatrix; /// - /// The angle of rotation in degrees. + /// The angle of processMatrix in degrees. /// private float angle; @@ -26,7 +24,7 @@ namespace ImageProcessorCore public override int Parallelism { get; set; } = 1; /// - /// Gets or sets the angle of rotation in degrees. + /// Gets or sets the angle of processMatrix in degrees. /// public float Angle { @@ -40,8 +38,6 @@ namespace ImageProcessorCore this.angle = value; } } - - /// /// Gets or sets a value indicating whether to expand the canvas to fit the rotated image. /// @@ -50,50 +46,26 @@ namespace ImageProcessorCore /// protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { + processMatrix = Point.CreateRotation(new Point(0, 0), -this.angle); if (this.Expand) { - // First find out how big the target rectangle should be. - Point centre = Rectangle.Center(sourceRectangle); - Matrix3x2 rotation = Point.CreateRotation(centre, -this.angle); - Rectangle rectangle = ImageMaths.GetBoundingRectangle(sourceRectangle, rotation); - target.SetPixels(rectangle.Width, rectangle.Height, new float[rectangle.Width * rectangle.Height * 4]); + processMatrix = Point.CreateRotation(new Point(0,0), -this.angle); + ProcessMatrixHelper.CreateNewTarget(target, sourceRectangle,processMatrix); } } /// protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { - - Matrix3x2 rotation = Point.CreateRotation(new Point(0, 0), -this.angle); - Matrix3x2 tran = Matrix3x2.CreateTranslation(-target.Width / 2f, -target.Height / 2f); - rotation = tran * rotation; - Matrix3x2 tran2 = Matrix3x2.CreateTranslation(source.Width / 2f, source.Height / 2f); - rotation = rotation * tran2; - - + var apply = ProcessMatrixHelper.Matrix3X2(target, source,processMatrix); Parallel.For( 0, target.Height, y => { - for (int x = 0; x < target.Width; x++) - { - // Rotate at the centre point - Point rotated = Point.Rotate(new Point(x, y), rotation); - if (source.Bounds.Contains(rotated.X, rotated.Y)) - { - target[x, y] = source[rotated.X, rotated.Y]; - } - } - - this.OnRowProcessed(); + ProcessMatrixHelper.DrawHorizontalData(target, source, y, apply); + OnRowProcessed(); }); } - - /// - protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) - { - - } } } \ No newline at end of file diff --git a/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs index 1bfcfb7e2..99b9f5457 100644 --- a/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs @@ -13,10 +13,7 @@ namespace ImageProcessorCore /// public class SkewProcessor : ImageSampler { - /// - /// The image used for storing the first pass pixels. - /// - private Image firstPass; + private Matrix3x2 processMatrix; /// /// The angle of rotation along the x-axis. @@ -43,16 +40,6 @@ namespace ImageProcessorCore set { - if (value > 360) - { - value -= 360; - } - - if (value < 0) - { - value += 360; - } - this.angleX = value; } } @@ -69,16 +56,6 @@ namespace ImageProcessorCore set { - if (value > 360) - { - value -= 360; - } - - if (value < 0) - { - value += 360; - } - this.angleY = value; } } @@ -96,69 +73,26 @@ namespace ImageProcessorCore /// protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { - // If we are expanding we need to pad the bounds of the source rectangle. - // We can use the resizer in nearest neighbor mode to do this fairly quickly. + processMatrix = Point.CreateSkew(new Point(0, 0), -this.angleX, -this.angleY); if (this.Expand) { - // First find out how big the target rectangle should be. - Point centre = this.Center == Point.Empty ? Rectangle.Center(sourceRectangle) : this.Center; - Matrix3x2 skew = Point.CreateSkew(centre, -this.angleX, -this.angleY); - Rectangle rectangle = ImageMaths.GetBoundingRectangle(sourceRectangle, skew); - ResizeOptions options = new ResizeOptions - { - Size = new Size(rectangle.Width, rectangle.Height), - Mode = ResizeMode.BoxPad - }; - - // Get the padded bounds and resize the image. - Rectangle bounds = ResizeHelper.CalculateTargetLocationAndBounds(source, options); - this.firstPass = new Image(rectangle.Width, rectangle.Height); - target.SetPixels(rectangle.Width, rectangle.Height, new float[rectangle.Width * rectangle.Height * 4]); - new ResizeProcessor(new NearestNeighborResampler()).Apply(this.firstPass, source, rectangle.Width, rectangle.Height, bounds, sourceRectangle); - } - else - { - // Just clone the pixels across. - this.firstPass = new Image(source.Width, source.Height); - this.firstPass.ClonePixels(source.Width, source.Height, source.Pixels); + ProcessMatrixHelper.CreateNewTarget(target, sourceRectangle, processMatrix); } } /// protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { - int height = this.firstPass.Height; - int startX = 0; - int endX = this.firstPass.Width; - Point centre = this.Center == Point.Empty ? Rectangle.Center(this.firstPass.Bounds) : this.Center; - Matrix3x2 skew = Point.CreateSkew(centre, -this.angleX, -this.angleY); - - // Since we are not working in parallel we use full height and width - // of the first pass image. + var apply = ProcessMatrixHelper.Matrix3X2(target, source, processMatrix); Parallel.For( 0, - height, + target.Height, y => { - for (int x = startX; x < endX; x++) - { - // Skew at the centre point - Point skewed = Point.Skew(new Point(x, y), skew); - if (this.firstPass.Bounds.Contains(skewed.X, skewed.Y)) - { - target[x, y] = this.firstPass[skewed.X, skewed.Y]; - } - } - - this.OnRowProcessed(); + ProcessMatrixHelper.DrawHorizontalData(target, source, y, apply); + OnRowProcessed(); }); - } - /// - protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) - { - // Cleanup. - this.firstPass.Dispose(); } } } \ No newline at end of file diff --git a/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs b/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs index 29ab3be26..cffc39427 100644 --- a/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs +++ b/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs @@ -471,7 +471,7 @@ using (Image image = new Image(stream)) using (FileStream output = File.OpenWrite($"TestOutput/Rotate/{filename}")) { - image.Rotate(20, this.ProgressUpdate) + image.Rotate(-170, this.ProgressUpdate) .Save(output); } @@ -500,7 +500,7 @@ using (Image image = new Image(stream)) using (FileStream output = File.OpenWrite($"TestOutput/Skew/{filename}")) { - image.Skew(20, 10, this.ProgressUpdate) + image.Skew(-20, -10, this.ProgressUpdate) .Save(output); } From e157cdc65ef25ebc48ef6880ae42bc7749a6dc6b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 5 Jul 2016 10:38:14 +1000 Subject: [PATCH 07/11] Update readme [skip ci] Former-commit-id: 1cea2c96a95e21ed20af6a16485793eb0a731322 Former-commit-id: 60baaa114618791b905f7ea2bb9b3a308cca25e1 Former-commit-id: 09c2b47f88b2b0d7cc71da45b3a561f147e5f4cc --- README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0ea2325cc..af25681e9 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,8 @@ We already have a [MyGet package repository](https://www.myget.org/gallery/image If you prefer, you can compile ImageProcessorCore yourself (please do and help!), you'll need: -- Visual Studio 2015 (or above) -- The [.NET Core SDK Installer -(Preview 1)](https://www.microsoft.com/net/download) - Click `.NET Core SDK Installer -(Preview 1)` -- The [.NET Core Tooling Preview 1 for Visual Studio 2015](https://dev.windows.com/en-us/downloads) - Click `.NET Core Tooling Preview 1 for Visual Studio 2015`. +- [Visual Studio 2015 with Update 3 (or above)](https://www.visualstudio.com/news/releasenotes/vs2015-update3-vs) +- The [.NET Core 1.0 SDK Installer](https://www.microsoft.com/net/core#windows) - Non VSCode link. To clone it locally click the "Clone in Windows" button above or run the following git commands. From d6db0366fc0231899acf699395a9ed8780a8544c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 5 Jul 2016 14:07:53 +1000 Subject: [PATCH 08/11] Remove task splitting code. Former-commit-id: a29d6e26e80dd4c47adfe6dab3cea8b41699165e Former-commit-id: 5463d0ceda62e577377de3ad9d52d347a830622b Former-commit-id: b3efff43e573ba7239d5724187cea5ae62f93690 --- .../Filters/Processors/AlphaProcessor.cs | 2 +- .../Processors/BackgroundColorProcessor.cs | 2 +- .../Binarization/ThresholdProcessor.cs | 2 +- .../Filters/Processors/BlendProcessor.cs | 2 +- .../Filters/Processors/BrightnessProcessor.cs | 2 +- .../ColorMatrix/ColorMatrixFilter.cs | 15 ++-- .../Filters/Processors/ContrastProcessor.cs | 2 +- .../Convolution/BoxBlurProcessor.cs | 3 - .../Convolution/Convolution2DFilter.cs | 2 +- .../Convolution/Convolution2PassFilter.cs | 43 ++++++------ .../Convolution/ConvolutionFilter.cs | 2 +- .../Convolution/GuassianBlurProcessor.cs | 3 - .../Convolution/GuassianSharpenProcessor.cs | 3 - .../Filters/Processors/GlowProcessor.cs | 2 +- .../Filters/Processors/InvertProcessor.cs | 2 +- .../Filters/Processors/PixelateProcessor.cs | 7 +- .../Filters/Processors/VignetteProcessor.cs | 2 +- ...lelImageProcessor.cs => ImageProcessor.cs} | 70 ++----------------- .../Samplers/Processors/CropProcessor.cs | 13 ++-- .../Samplers/Processors/ImageSampler.cs | 2 +- .../Samplers/Processors/ResizeProcessor.cs | 11 +-- .../Processors/RotateFlipProcessor.cs | 3 - .../Samplers/Processors/RotateProcessor.cs | 3 - .../Samplers/Processors/SkewProcessor.cs | 3 - 24 files changed, 51 insertions(+), 150 deletions(-) rename src/ImageProcessorCore/{ParallelImageProcessor.cs => ImageProcessor.cs} (69%) diff --git a/src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs b/src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs index 72ba79f9b..8ed703e03 100644 --- a/src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs @@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors /// /// An to change the Alpha of an . /// - public class AlphaProcessor : ParallelImageProcessor + public class AlphaProcessor : ImageProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs b/src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs index ef451a89d..e78987d23 100644 --- a/src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs @@ -11,7 +11,7 @@ namespace ImageProcessorCore.Processors /// /// Sets the background color of the image. /// - public class BackgroundColorProcessor : ParallelImageProcessor + public class BackgroundColorProcessor : ImageProcessor { /// /// The epsilon for comparing floating point numbers. diff --git a/src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs index dc3816a03..60b49d0ee 100644 --- a/src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs @@ -13,7 +13,7 @@ namespace ImageProcessorCore.Processors /// . The image will be converted to greyscale before thresholding /// occurs. /// - public class ThresholdProcessor : ParallelImageProcessor + public class ThresholdProcessor : ImageProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs b/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs index cbf966a7c..5de0741fa 100644 --- a/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs @@ -10,7 +10,7 @@ namespace ImageProcessorCore.Processors /// /// Combines two images together by blending the pixels. /// - public class BlendProcessor : ParallelImageProcessor + public class BlendProcessor : ImageProcessor { /// /// The image to blend. diff --git a/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs b/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs index a100e5717..6710ff593 100644 --- a/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs @@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors /// /// An to change the brightness of an . /// - public class BrightnessProcessor : ParallelImageProcessor + public class BrightnessProcessor : ImageProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs index eb01c3c22..d27ea5c6d 100644 --- a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs @@ -11,7 +11,7 @@ namespace ImageProcessorCore.Processors /// /// The color matrix filter. /// - public abstract class ColorMatrixFilter : ParallelImageProcessor, IColorMatrixFilter + public abstract class ColorMatrixFilter : ImageProcessor, IColorMatrixFilter { /// public abstract Matrix4x4 Matrix { get; } @@ -22,8 +22,6 @@ namespace ImageProcessorCore.Processors /// protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { - int sourceY = sourceRectangle.Y; - int sourceBottom = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; Matrix4x4 matrix = this.Matrix; @@ -36,15 +34,12 @@ namespace ImageProcessorCore.Processors endY, y => { - if (y >= sourceY && y < sourceBottom) + for (int x = startX; x < endX; x++) { - for (int x = startX; x < endX; x++) - { - targetPixels[x, y] = this.ApplyMatrix(sourcePixels[x, y], matrix); - } - - this.OnRowProcessed(); + targetPixels[x, y] = this.ApplyMatrix(sourcePixels[x, y], matrix); } + + this.OnRowProcessed(); }); } } diff --git a/src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs index 920a6fb01..19f564be4 100644 --- a/src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs @@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors /// /// An to change the contrast of an . /// - public class ContrastProcessor : ParallelImageProcessor + public class ContrastProcessor : ImageProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs index 4ad60d65b..042288ffd 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs @@ -42,9 +42,6 @@ namespace ImageProcessorCore.Processors /// public override float[,] KernelY => this.kernelY; - /// - public override int Parallelism => 1; - /// protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs index 0dad68485..b16a528db 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs @@ -11,7 +11,7 @@ namespace ImageProcessorCore.Processors /// /// Defines a filter that uses two one-dimensional matrices to perform convolution against an image. /// - public abstract class Convolution2DFilter : ParallelImageProcessor + public abstract class Convolution2DFilter : ImageProcessor { /// /// Gets the horizontal gradient operator. diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs index 66e8cac98..c9031dfd7 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs @@ -10,7 +10,7 @@ namespace ImageProcessorCore.Processors /// /// Defines a filter that uses two one-dimensional matrices to perform two-pass convolution against an image. /// - public abstract class Convolution2PassFilter : ParallelImageProcessor + public abstract class Convolution2PassFilter : ImageProcessor { /// /// Gets the horizontal gradient operator. @@ -64,7 +64,6 @@ namespace ImageProcessorCore.Processors int radiusY = kernelHeight >> 1; int radiusX = kernelWidth >> 1; - int sourceY = sourceRectangle.Y; int sourceBottom = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; @@ -79,36 +78,34 @@ namespace ImageProcessorCore.Processors endY, y => { - if (y >= sourceY && y < sourceBottom) + for (int x = startX; x < endX; x++) { - for (int x = startX; x < endX; x++) - { - Color destination = new Color(); + Color destination = new Color(); - // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelHeight; fy++) - { - int fyr = fy - radiusY; - int offsetY = y + fyr; + // Apply each matrix multiplier to the color components for each pixel. + for (int fy = 0; fy < kernelHeight; fy++) + { + int fyr = fy - radiusY; + int offsetY = y + fyr; - offsetY = offsetY.Clamp(0, maxY); + offsetY = offsetY.Clamp(0, maxY); - for (int fx = 0; fx < kernelWidth; fx++) - { - int fxr = fx - radiusX; - int offsetX = x + fxr; + for (int fx = 0; fx < kernelWidth; fx++) + { + int fxr = fx - radiusX; + int offsetX = x + fxr; - offsetX = offsetX.Clamp(0, maxX); + offsetX = offsetX.Clamp(0, maxX); - Color currentColor = sourcePixels[offsetX, offsetY]; - destination += kernel[fy, fx] * currentColor; - } + Color currentColor = sourcePixels[offsetX, offsetY]; + destination += kernel[fy, fx] * currentColor; } - - targetPixels[x, y] = destination; } - this.OnRowProcessed(); + + targetPixels[x, y] = destination; } + + this.OnRowProcessed(); }); } } diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs index 84884ff83..8ddcab4a5 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs @@ -10,7 +10,7 @@ namespace ImageProcessorCore.Processors /// /// Defines a filter that uses a 2 dimensional matrix to perform convolution against an image. /// - public abstract class ConvolutionFilter : ParallelImageProcessor + public abstract class ConvolutionFilter : ImageProcessor { /// /// Gets the 2d gradient operator. diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs index 904de5994..6288ea963 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs @@ -76,9 +76,6 @@ namespace ImageProcessorCore.Processors /// public override float[,] KernelY => this.kernelY; - /// - public override int Parallelism => 1; - /// protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs index 8d95eaf82..9d70732d2 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs @@ -78,9 +78,6 @@ namespace ImageProcessorCore.Processors /// public override float[,] KernelY => this.kernelY; - /// - public override int Parallelism => 1; - /// protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { diff --git a/src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs b/src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs index 63c18e9eb..e046be6a9 100644 --- a/src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs @@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors /// /// Creates a glow effect on the image /// - public class GlowProcessor : ParallelImageProcessor + public class GlowProcessor : ImageProcessor { /// /// Gets or sets the glow color to apply. diff --git a/src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs b/src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs index 31ef3ceb8..1f319b92b 100644 --- a/src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs @@ -11,7 +11,7 @@ namespace ImageProcessorCore.Processors /// /// An to invert the colors of an . /// - public class InvertProcessor : ParallelImageProcessor + public class InvertProcessor : ImageProcessor { /// protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) diff --git a/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs b/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs index 6c129bbee..4769799b5 100644 --- a/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs @@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors /// /// An to invert the colors of an . /// - public class PixelateProcessor : ParallelImageProcessor + public class PixelateProcessor : ImageProcessor { /// /// Initializes a new instance of the class. @@ -27,9 +27,6 @@ namespace ImageProcessorCore.Processors this.Value = size; } - /// - public override int Parallelism { get; set; } = 1; - /// /// Gets or the pixel size. /// @@ -59,7 +56,6 @@ namespace ImageProcessorCore.Processors { for (int x = startX; x < endX; x += size) { - int offsetX = offset; int offsetY = offset; @@ -88,6 +84,7 @@ namespace ImageProcessorCore.Processors } } } + this.OnRowProcessed(); } }); diff --git a/src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs b/src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs index 6a1bbc684..1fd4630ed 100644 --- a/src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs @@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors /// /// Creates a vignette effect on the image /// - public class VignetteProcessor : ParallelImageProcessor + public class VignetteProcessor : ImageProcessor { /// /// Gets or sets the vignette color to apply. diff --git a/src/ImageProcessorCore/ParallelImageProcessor.cs b/src/ImageProcessorCore/ImageProcessor.cs similarity index 69% rename from src/ImageProcessorCore/ParallelImageProcessor.cs rename to src/ImageProcessorCore/ImageProcessor.cs index a6b9fc354..ea76ebbf8 100644 --- a/src/ImageProcessorCore/ParallelImageProcessor.cs +++ b/src/ImageProcessorCore/ImageProcessor.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -7,21 +7,15 @@ namespace ImageProcessorCore.Processors { using System; using System.Threading; - using System.Threading.Tasks; /// - /// Allows the application of processors using parallel processing. + /// Allows the application of processors to images. /// - public abstract class ParallelImageProcessor : IImageProcessor + public abstract class ImageProcessor : IImageProcessor { /// public event ProgressEventHandler OnProgress; - /// - /// Gets or sets the count of workers to run the process in parallel. - /// - public virtual int Parallelism { get; set; } = Environment.ProcessorCount * 2; - /// /// The number of rows processed by a derived class. /// @@ -42,34 +36,7 @@ namespace ImageProcessorCore.Processors this.numRowsProcessed = 0; this.totalRows = sourceRectangle.Height; - if (this.Parallelism > 1) - { - int partitionCount = this.Parallelism; - - Task[] tasks = new Task[partitionCount]; - - for (int p = 0; p < partitionCount; p++) - { - int current = p; - tasks[p] = Task.Run( - () => - { - int batchSize = sourceRectangle.Height / partitionCount; - int yStart = sourceRectangle.Y + (current * batchSize); - int yEnd = current == partitionCount - 1 - ? sourceRectangle.Bottom - : yStart + batchSize; - - this.Apply(target, source, target.Bounds, sourceRectangle, yStart, yEnd); - }); - } - - Task.WaitAll(tasks); - } - else - { - this.Apply(target, source, target.Bounds, sourceRectangle, sourceRectangle.Y, sourceRectangle.Bottom); - } + this.Apply(target, source, target.Bounds, sourceRectangle, sourceRectangle.Y, sourceRectangle.Bottom); this.AfterApply(target, source, target.Bounds, sourceRectangle); } @@ -88,6 +55,7 @@ namespace ImageProcessorCore.Processors float[] pixels = new float[width * height * 4]; target.SetPixels(width, height, pixels); + // Ensure we always have bounds. if (sourceRectangle == Rectangle.Empty) { sourceRectangle = source.Bounds; @@ -101,33 +69,9 @@ namespace ImageProcessorCore.Processors this.OnApply(target, source, targetRectangle, sourceRectangle); this.numRowsProcessed = 0; - this.totalRows = targetRectangle.Bottom; + this.totalRows = targetRectangle.Height; - if (this.Parallelism > 1) - { - int partitionCount = this.Parallelism; - - Task[] tasks = new Task[partitionCount]; - - for (int p = 0; p < partitionCount; p++) - { - int current = p; - tasks[p] = Task.Run(() => - { - int batchSize = targetRectangle.Bottom / partitionCount; - int yStart = current * batchSize; - int yEnd = current == partitionCount - 1 ? targetRectangle.Bottom : yStart + batchSize; - - this.Apply(target, source, targetRectangle, sourceRectangle, yStart, yEnd); - }); - } - - Task.WaitAll(tasks); - } - else - { - this.Apply(target, source, targetRectangle, sourceRectangle, targetRectangle.Y, targetRectangle.Bottom); - } + this.Apply(target, source, targetRectangle, sourceRectangle, targetRectangle.Y, targetRectangle.Bottom); this.AfterApply(target, source, target.Bounds, sourceRectangle); } diff --git a/src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs index e6707afa0..adcd3c180 100644 --- a/src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs @@ -15,8 +15,6 @@ namespace ImageProcessorCore.Processors /// protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { - int targetY = targetRectangle.Y; - int targetBottom = targetRectangle.Bottom; int startX = targetRectangle.X; int endX = targetRectangle.Right; int sourceX = sourceRectangle.X; @@ -30,15 +28,12 @@ namespace ImageProcessorCore.Processors endY, y => { - if (y >= targetY && y < targetBottom) + for (int x = startX; x < endX; x++) { - for (int x = startX; x < endX; x++) - { - targetPixels[x, y] = sourcePixels[x + sourceX, y + sourceY]; - } - - this.OnRowProcessed(); + targetPixels[x, y] = sourcePixels[x + sourceX, y + sourceY]; } + + this.OnRowProcessed(); }); } } diff --git a/src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs b/src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs index cc8bfe4cc..adfe77432 100644 --- a/src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs +++ b/src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs @@ -9,7 +9,7 @@ namespace ImageProcessorCore.Processors /// Applies sampling methods to an image. /// All processors requiring resampling or resizing should inherit from this. /// - public abstract class ImageSampler : ParallelImageProcessor, IImageSampler + public abstract class ImageSampler : ImageProcessor, IImageSampler { /// public virtual bool Compand { get; set; } = false; diff --git a/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs index 82b07d40b..0431dc95e 100644 --- a/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs @@ -31,9 +31,6 @@ namespace ImageProcessorCore.Processors this.Sampler = sampler; } - /// - public override int Parallelism { get; set; } = 1; - /// /// Gets the sampler to perform the resize operation. /// @@ -62,13 +59,7 @@ namespace ImageProcessorCore.Processors } /// - protected override void Apply( - ImageBase target, - ImageBase source, - Rectangle targetRectangle, - Rectangle sourceRectangle, - int startY, - int endY) + protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { // Jump out, we'll deal with that later. if (source.Bounds == target.Bounds && sourceRectangle == targetRectangle) diff --git a/src/ImageProcessorCore/Samplers/Processors/RotateFlipProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/RotateFlipProcessor.cs index 263242fc1..6ef8866ba 100644 --- a/src/ImageProcessorCore/Samplers/Processors/RotateFlipProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/RotateFlipProcessor.cs @@ -34,9 +34,6 @@ namespace ImageProcessorCore.Processors /// public RotateType RotateType { get; } - /// - public override int Parallelism { get; set; } = 1; - /// protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { diff --git a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs index d12bd3ec2..7aafe0e08 100644 --- a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs @@ -18,9 +18,6 @@ namespace ImageProcessorCore.Processors /// private Matrix3x2 processMatrix; - /// - public override int Parallelism { get; set; } = 1; - /// /// Gets or sets the angle of processMatrix in degrees. /// diff --git a/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs index 43e54a1c7..02d5dd9e4 100644 --- a/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs @@ -18,9 +18,6 @@ namespace ImageProcessorCore.Processors /// private Matrix3x2 processMatrix; - /// - public override int Parallelism { get; set; } = 1; - /// /// Gets or sets the angle of rotation along the x-axis in degrees. /// From 64d390681e86367747f6dccfada620d0803dc185 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 5 Jul 2016 14:18:47 +1000 Subject: [PATCH 09/11] Fix after method. Former-commit-id: 5f9d7336f76a2caa586b400bef7fd96cde06bc77 Former-commit-id: 22493cbf1d2ad4f816715faff23ac0af1af26a01 Former-commit-id: 6ec88244fefd04fdb3758020e91ebd2e88dd08a9 --- .../Filters/Processors/ColorMatrix/LomographProcessor.cs | 2 +- .../Filters/Processors/ColorMatrix/PolaroidProcessor.cs | 2 +- .../Samplers/Processors/EntropyCropProcessor.cs | 2 +- src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs index 4b507fcd1..242299433 100644 --- a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs @@ -24,7 +24,7 @@ namespace ImageProcessorCore.Processors }; /// - protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) + protected override void AfterApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { new VignetteProcessor { Color = new Color(0, 10 / 255f, 0) }.Apply(target, target, targetRectangle); } diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs index dce2609e5..ea6f85a39 100644 --- a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs @@ -30,7 +30,7 @@ namespace ImageProcessorCore.Processors }; /// - protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) + protected override void AfterApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { new VignetteProcessor { Color = new Color(102 / 255f, 34 / 255f, 0) }.Apply(target, target, targetRectangle); new GlowProcessor diff --git a/src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs index e0ffb29e1..f9f8601f5 100644 --- a/src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs @@ -92,7 +92,7 @@ namespace ImageProcessorCore.Processors } /// - protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) + protected override void AfterApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { // Copy the pixels over. if (source.Bounds == target.Bounds) diff --git a/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs index 0431dc95e..95ead2bf0 100644 --- a/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs @@ -208,7 +208,7 @@ namespace ImageProcessorCore.Processors } /// - protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) + protected override void AfterApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) { // Copy the pixels over. if (source.Bounds == target.Bounds && sourceRectangle == targetRectangle) From 3ca3ad31f1810cbfb4e7b743cd71617f6a7725a1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 5 Jul 2016 15:44:41 +1000 Subject: [PATCH 10/11] Add TODO: for unmanaged pixel copying [skip ci] Former-commit-id: 45772bc399b25280ea79cd5afc081945b45808a8 Former-commit-id: f14063e037c5cc3c7bd2d1e9c0abf6d7413034dd Former-commit-id: 7f4addaec89fea153275167ac093716192995108 --- src/ImageProcessorCore/PixelAccessor.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ImageProcessorCore/PixelAccessor.cs b/src/ImageProcessorCore/PixelAccessor.cs index 47a179731..3f40f4b3c 100644 --- a/src/ImageProcessorCore/PixelAccessor.cs +++ b/src/ImageProcessorCore/PixelAccessor.cs @@ -63,6 +63,9 @@ namespace ImageProcessorCore { fixed (float* pbuffer = image.Pixels) { + // TODO: This isn't actually copying. + // We need to allocate and copy the pixels into unmanaged memory. + // Will need to research how to do this. this.pixelsBase = pbuffer; } } From bc36435122c89c58e127a460ddfd3102b522eb84 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 5 Jul 2016 23:33:45 +1000 Subject: [PATCH 11/11] Use direct access to Color to get/set pixels. Former-commit-id: 38f45a1ac9325aabbeb25c0422f973bf44d7fe65 Former-commit-id: 55b3f1b83610fe278d3af85a855689319c37eba9 Former-commit-id: a2e726b811db365bbb164d3b30b8232da842636b --- Rebracer.xml | 8 ++++++-- src/ImageProcessorCore/PixelAccessor.cs | 25 +++++-------------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/Rebracer.xml b/Rebracer.xml index 8497d212e..9b0a654d2 100644 --- a/Rebracer.xml +++ b/Rebracer.xml @@ -22,8 +22,12 @@ + 0 + 0 1 - 1 + 1 + -1 + -1 0 1 0 @@ -81,7 +85,7 @@ 1 0 1 - 1 + 0 0 0 1 diff --git a/src/ImageProcessorCore/PixelAccessor.cs b/src/ImageProcessorCore/PixelAccessor.cs index 3f40f4b3c..2553b2536 100644 --- a/src/ImageProcessorCore/PixelAccessor.cs +++ b/src/ImageProcessorCore/PixelAccessor.cs @@ -16,7 +16,7 @@ namespace ImageProcessorCore /// /// The position of the first pixel in the bitmap. /// - private float* pixelsBase; + private Color* pixelsBase; /// /// Provides a way to access the pixels from unmanaged memory. @@ -48,27 +48,12 @@ namespace ImageProcessorCore Guard.MustBeGreaterThan(image.Width, 0, "image width"); Guard.MustBeGreaterThan(image.Height, 0, "image height"); - int size = image.Pixels.Length; this.Width = image.Width; this.Height = image.Height; // Assign the pointer. - // If buffer is allocated on Large Object Heap i.e > 85Kb, then we are going to pin it instead of making a copy. - if (size > 87040) - { - this.pixelsHandle = GCHandle.Alloc(image.Pixels, GCHandleType.Pinned); - this.pixelsBase = (float*)this.pixelsHandle.AddrOfPinnedObject().ToPointer(); - } - else - { - fixed (float* pbuffer = image.Pixels) - { - // TODO: This isn't actually copying. - // We need to allocate and copy the pixels into unmanaged memory. - // Will need to research how to do this. - this.pixelsBase = pbuffer; - } - } + this.pixelsHandle = GCHandle.Alloc(image.Pixels, GCHandleType.Pinned); + this.pixelsBase = (Color*)this.pixelsHandle.AddrOfPinnedObject().ToPointer(); } /// @@ -116,7 +101,7 @@ namespace ImageProcessorCore throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height."); } #endif - return *((Color*)(this.pixelsBase + (((y * this.Width) + x) * 4))); + return *(this.pixelsBase + ((y * this.Width) + x)); } set @@ -132,7 +117,7 @@ namespace ImageProcessorCore throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height."); } #endif - *(Color*)(this.pixelsBase + (((y * this.Width) + x) * 4)) = value; + *(this.pixelsBase + ((y * this.Width) + x)) = value; } }