diff --git a/src/ImageProcessorCore/Samplers/Crop.cs b/src/ImageProcessorCore/Samplers/Crop.cs new file mode 100644 index 000000000..6ff3f49c0 --- /dev/null +++ b/src/ImageProcessorCore/Samplers/Crop.cs @@ -0,0 +1,76 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// ------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessorCore +{ + using Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Crops an image to the given width and height. + /// + /// The pixel format. + /// The packed format. long, float. + /// The image to resize. + /// The target image width. + /// The target image height. + /// A delegate which is called as progress is made processing the image. + /// The + public static Image Crop(this Image source, int width, int height, ProgressEventHandler progressHandler = null) + where T : IPackedVector + where TP : struct + { + return Crop(source, width, height, source.Bounds, progressHandler); + } + + /// + /// Crops an image to the given width and height with the given source rectangle. + /// + /// If the source rectangle is smaller than the target dimensions then the + /// area within the source is resized performing a zoomed crop. + /// + /// + /// The pixel format. + /// The packed format. long, float. + /// The image to crop. + /// The target image width. + /// The target image height. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// A delegate which is called as progress is made processing the image. + /// The + public static Image Crop(this Image source, int width, int height, Rectangle sourceRectangle, ProgressEventHandler progressHandler = null) + where T : IPackedVector + where TP : struct + { + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + + if (sourceRectangle.Width < width || sourceRectangle.Height < height) + { + // If the source rectangle is smaller than the target perform a + // cropped zoom. + source = source.Resize(sourceRectangle.Width, sourceRectangle.Height); + } + + CropProcessor processor = new CropProcessor(); + processor.OnProgress += progressHandler; + + try + { + return source.Process(width, height, sourceRectangle, new Rectangle(0, 0, width, height), processor); + } + finally + { + processor.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs new file mode 100644 index 000000000..2029c33bd --- /dev/null +++ b/src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs @@ -0,0 +1,41 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + using System.Threading.Tasks; + + /// + /// Provides methods to allow the cropping of an image. + /// + public class CropProcessor : ImageSampler + { + /// + protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) + { + int startX = targetRectangle.X; + int endX = targetRectangle.Right; + int sourceX = sourceRectangle.X; + int sourceY = sourceRectangle.Y; + + using (IPixelAccessor sourcePixels = source.Lock()) + using (IPixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + startY, + endY, + y => + { + for (int x = startX; x < endX; x++) + { + targetPixels[x, y] = sourcePixels[x + sourceX, y + sourceY]; + } + + this.OnRowProcessed(); + }); + } + } + } +} diff --git a/src/ImageProcessorCore/Samplers/Resize.cs b/src/ImageProcessorCore/Samplers/Resize.cs index 8e5405870..aa2089d07 100644 --- a/src/ImageProcessorCore/Samplers/Resize.cs +++ b/src/ImageProcessorCore/Samplers/Resize.cs @@ -8,20 +8,21 @@ namespace ImageProcessorCore using Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Resizes an image in accordance with the given . /// - /// The type of pixels contained within the image. + /// The pixel format. + /// The packed format. long, float. /// The image to resize. /// The resize options. /// A delegate which is called as progress is made processing the image. - /// The + /// The /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, ResizeOptions options, ProgressEventHandler progressHandler = null) + public static Image Resize(this Image source, ResizeOptions options, ProgressEventHandler progressHandler = null) where T : IPackedVector where TP : struct { @@ -44,14 +45,15 @@ namespace ImageProcessorCore /// /// Resizes an image to the given width and height. /// - /// The type of pixels contained within the image. + /// The pixel format. + /// The packed format. long, float. /// The image to resize. /// The target image width. /// The target image height. /// A delegate which is called as progress is made processing the image. - /// The + /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height, ProgressEventHandler progressHandler = null) + public static Image Resize(this Image source, int width, int height, ProgressEventHandler progressHandler = null) where T : IPackedVector where TP : struct { @@ -61,15 +63,16 @@ namespace ImageProcessorCore /// /// Resizes an image to the given width and height. /// - /// The type of pixels contained within the image. + /// The pixel format. + /// The packed format. long, float. /// The image to resize. /// The target image width. /// The target image height. /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// A delegate which is called as progress is made processing the image. - /// The + /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height, bool compand, ProgressEventHandler progressHandler = null) + public static Image Resize(this Image source, int width, int height, bool compand, ProgressEventHandler progressHandler = null) where T : IPackedVector where TP : struct { @@ -79,16 +82,17 @@ namespace ImageProcessorCore /// /// Resizes an image to the given width and height with the given sampler. /// - /// The type of pixels contained within the image. + /// The pixel format. + /// The packed format. long, float. /// The image to resize. /// The target image width. /// The target image height. /// The to perform the resampling. /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// A delegate which is called as progress is made processing the image. - /// The + /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height, IResampler sampler, bool compand, ProgressEventHandler progressHandler = null) + public static Image Resize(this Image source, int width, int height, IResampler sampler, bool compand, ProgressEventHandler progressHandler = null) where T : IPackedVector where TP : struct { @@ -99,7 +103,8 @@ namespace ImageProcessorCore /// Resizes an image to the given width and height with the given sampler and /// source rectangle. /// - /// The type of pixels contained within the image. + /// The pixel format. + /// The packed format. long, float. /// The image to resize. /// The target image width. /// The target image height. @@ -112,9 +117,9 @@ namespace ImageProcessorCore /// /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// A delegate which is called as progress is made processing the image. - /// The + /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height, IResampler sampler, Rectangle sourceRectangle, Rectangle targetRectangle, bool compand = false, ProgressEventHandler progressHandler = null) + public static Image Resize(this Image source, int width, int height, IResampler sampler, Rectangle sourceRectangle, Rectangle targetRectangle, bool compand = false, ProgressEventHandler progressHandler = null) where T : IPackedVector where TP : struct { diff --git a/tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs b/tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs index 5eae5cdef..4eaeea6ec 100644 --- a/tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs +++ b/tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs @@ -1,40 +1,40 @@ -//namespace ImageProcessorCore.Benchmarks -//{ -// using System.Drawing; -// using System.Drawing.Drawing2D; +namespace ImageProcessorCore.Benchmarks +{ + using System.Drawing; + using System.Drawing.Drawing2D; -// using BenchmarkDotNet.Attributes; -// using CoreImage = ImageProcessorCore.Image; -// using CoreSize = ImageProcessorCore.Size; + using BenchmarkDotNet.Attributes; + using CoreSize = ImageProcessorCore.Size; + using CoreImage = ImageProcessorCore.Image; -// public class Crop -// { -// [Benchmark(Baseline = true, Description = "System.Drawing Crop")] -// public Size CropSystemDrawing() -// { -// using (Bitmap source = new Bitmap(400, 400)) -// { -// using (Bitmap destination = new Bitmap(100, 100)) -// { -// using (Graphics graphics = Graphics.FromImage(destination)) -// { -// graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; -// graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; -// graphics.CompositingQuality = CompositingQuality.HighQuality; -// graphics.DrawImage(source, new Rectangle(0, 0, 100, 100), 0, 0, 100, 100, GraphicsUnit.Pixel); -// } + public class Crop + { + [Benchmark(Baseline = true, Description = "System.Drawing Crop")] + public Size CropSystemDrawing() + { + using (Bitmap source = new Bitmap(400, 400)) + { + using (Bitmap destination = new Bitmap(100, 100)) + { + using (Graphics graphics = Graphics.FromImage(destination)) + { + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.DrawImage(source, new Rectangle(0, 0, 100, 100), 0, 0, 100, 100, GraphicsUnit.Pixel); + } -// return destination.Size; -// } -// } -// } + return destination.Size; + } + } + } -// [Benchmark(Description = "ImageProcessorCore Crop")] -// public CoreSize CropResizeCore() -// { -// CoreImage image = new CoreImage(400, 400); -// image.Crop(100, 100); -// return new CoreSize(image.Width, image.Height); -// } -// } -//} + [Benchmark(Description = "ImageProcessorCore Crop")] + public CoreSize CropResizeCore() + { + CoreImage image = new CoreImage(400, 400); + image.Crop(100, 100); + return new CoreSize(image.Width, image.Height); + } + } +}