Browse Source

Merge remote-tracking branch 'refs/remotes/origin/Core' into Core-Flava

Former-commit-id: 6583ef8501a525fcc1c5103ad09dadea0045e574
Former-commit-id: 6e6ddc0dbf12d32958e50d9b6d04548d0bd5106b
Former-commit-id: 46c9e0018442621454a9ac461d04b218d39270ed
pull/1/head
James Jackson-South 10 years ago
parent
commit
820cac4d12
  1. 7
      README.md
  2. 8
      Rebracer.xml
  3. 2
      src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs
  4. 2
      src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs
  5. 2
      src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs
  6. 2
      src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs
  7. 2
      src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs
  8. 15
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs
  9. 2
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs
  10. 2
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs
  11. 2
      src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs
  12. 3
      src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs
  13. 2
      src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs
  14. 43
      src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs
  15. 2
      src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs
  16. 3
      src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs
  17. 3
      src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs
  18. 2
      src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs
  19. 2
      src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs
  20. 7
      src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs
  21. 2
      src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs
  22. 70
      src/ImageProcessorCore/ImageProcessor.cs
  23. 22
      src/ImageProcessorCore/PixelAccessor.cs
  24. 13
      src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs
  25. 2
      src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs
  26. 2
      src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs
  27. 47
      src/ImageProcessorCore/Samplers/Processors/Matrix3x2Processor.cs
  28. 13
      src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs
  29. 3
      src/ImageProcessorCore/Samplers/Processors/RotateFlipProcessor.cs
  30. 64
      src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs
  31. 68
      src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs
  32. 4
      src/ImageProcessorCore/Samplers/Resamplers/CatmullRomResampler.cs
  33. 3
      src/ImageProcessorCore/Samplers/Resamplers/HermiteResampler.cs
  34. 1
      src/ImageProcessorCore/Samplers/Resamplers/Lanczos3Resampler.cs
  35. 1
      src/ImageProcessorCore/Samplers/Resamplers/Lanczos5Resampler.cs
  36. 1
      src/ImageProcessorCore/Samplers/Resamplers/Lanczos8Resampler.cs
  37. 11
      src/ImageProcessorCore/Samplers/Rotate.cs
  38. 11
      src/ImageProcessorCore/Samplers/Skew.cs
  39. 4
      tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs

7
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.

8
Rebracer.xml

@ -22,8 +22,12 @@
</ToolsOptionsCategory>
<ToolsOptionsCategory name="TextEditor">
<ToolsOptionsSubCategory name="CSharp-Specific">
<PropertyValue name="AddImport_SuggestForTypesInNuGetPackages">0</PropertyValue>
<PropertyValue name="AddImport_SuggestForTypesInReferenceAssemblies">0</PropertyValue>
<PropertyValue name="AutoComment">1</PropertyValue>
<PropertyValue name="ClosedFileDiagnostics">1</PropertyValue>
<PropertyValue name="AutoInsertAsteriskForNewLinesOfBlockComments">1</PropertyValue>
<PropertyValue name="CSharpClosedFileDiagnostics">-1</PropertyValue>
<PropertyValue name="ClosedFileDiagnostics">-1</PropertyValue>
<PropertyValue name="DisplayLineSeparators">0</PropertyValue>
<PropertyValue name="EnableHighlightRelatedKeywords">1</PropertyValue>
<PropertyValue name="ExtractMethod_AllowMovingDeclaration">0</PropertyValue>
@ -81,7 +85,7 @@
<PropertyValue name="Style_PreferIntrinsicPredefinedTypeKeywordInMemberAccess">1</PropertyValue>
<PropertyValue name="Style_QualifyMemberAccessWithThisOrMe">0</PropertyValue>
<PropertyValue name="Style_UseVarWhenDeclaringLocals">1</PropertyValue>
<PropertyValue name="WarnOnBuildErrors">1</PropertyValue>
<PropertyValue name="WarnOnBuildErrors">0</PropertyValue>
<PropertyValue name="Wrapping_IgnoreSpacesAroundBinaryOperators">0</PropertyValue>
<PropertyValue name="Wrapping_IgnoreSpacesAroundVariableDeclaration">0</PropertyValue>
<PropertyValue name="Wrapping_KeepStatementsOnSingleLine">1</PropertyValue>

2
src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs

@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// An <see cref="IImageProcessor"/> to change the Alpha of an <see cref="Image"/>.
/// </summary>
public class AlphaProcessor : ParallelImageProcessor
public class AlphaProcessor : ImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="AlphaProcessor"/> class.

2
src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs

@ -11,7 +11,7 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Sets the background color of the image.
/// </summary>
public class BackgroundColorProcessor : ParallelImageProcessor
public class BackgroundColorProcessor : ImageProcessor
{
/// <summary>
/// The epsilon for comparing floating point numbers.

2
src/ImageProcessorCore/Filters/Processors/Binarization/ThresholdProcessor.cs

@ -13,7 +13,7 @@ namespace ImageProcessorCore.Processors
/// <see cref="Image"/>. The image will be converted to greyscale before thresholding
/// occurs.
/// </summary>
public class ThresholdProcessor : ParallelImageProcessor
public class ThresholdProcessor : ImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="ThresholdProcessor"/> class.

2
src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs

@ -10,7 +10,7 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Combines two images together by blending the pixels.
/// </summary>
public class BlendProcessor : ParallelImageProcessor
public class BlendProcessor : ImageProcessor
{
/// <summary>
/// The image to blend.

2
src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs

@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// An <see cref="IImageProcessor"/> to change the brightness of an <see cref="Image"/>.
/// </summary>
public class BrightnessProcessor : ParallelImageProcessor
public class BrightnessProcessor : ImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="BrightnessProcessor"/> class.

15
src/ImageProcessorCore/Filters/Processors/ColorMatrix/ColorMatrixFilter.cs

@ -11,7 +11,7 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// The color matrix filter.
/// </summary>
public abstract class ColorMatrixFilter : ParallelImageProcessor, IColorMatrixFilter
public abstract class ColorMatrixFilter : ImageProcessor, IColorMatrixFilter
{
/// <inheritdoc/>
public abstract Matrix4x4 Matrix { get; }
@ -22,8 +22,6 @@ namespace ImageProcessorCore.Processors
/// <inheritdoc/>
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();
});
}
}

2
src/ImageProcessorCore/Filters/Processors/ColorMatrix/LomographProcessor.cs

@ -24,7 +24,7 @@ namespace ImageProcessorCore.Processors
};
/// <inheritdoc/>
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);
}

2
src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs

@ -30,7 +30,7 @@ namespace ImageProcessorCore.Processors
};
/// <inheritdoc/>
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

2
src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs

@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// An <see cref="IImageProcessor"/> to change the contrast of an <see cref="Image"/>.
/// </summary>
public class ContrastProcessor : ParallelImageProcessor
public class ContrastProcessor : ImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="ContrastProcessor"/> class.

3
src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs

@ -42,9 +42,6 @@ namespace ImageProcessorCore.Processors
/// <inheritdoc/>
public override float[,] KernelY => this.kernelY;
/// <inheritdoc/>
public override int Parallelism => 1;
/// <inheritdoc/>
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
{

2
src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2DFilter.cs

@ -11,7 +11,7 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Defines a filter that uses two one-dimensional matrices to perform convolution against an image.
/// </summary>
public abstract class Convolution2DFilter : ParallelImageProcessor
public abstract class Convolution2DFilter : ImageProcessor
{
/// <summary>
/// Gets the horizontal gradient operator.

43
src/ImageProcessorCore/Filters/Processors/Convolution/Convolution2PassFilter.cs

@ -10,7 +10,7 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Defines a filter that uses two one-dimensional matrices to perform two-pass convolution against an image.
/// </summary>
public abstract class Convolution2PassFilter : ParallelImageProcessor
public abstract class Convolution2PassFilter : ImageProcessor
{
/// <summary>
/// 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();
});
}
}

2
src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs

@ -10,7 +10,7 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Defines a filter that uses a 2 dimensional matrix to perform convolution against an image.
/// </summary>
public abstract class ConvolutionFilter : ParallelImageProcessor
public abstract class ConvolutionFilter : ImageProcessor
{
/// <summary>
/// Gets the 2d gradient operator.

3
src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs

@ -76,9 +76,6 @@ namespace ImageProcessorCore.Processors
/// <inheritdoc/>
public override float[,] KernelY => this.kernelY;
/// <inheritdoc/>
public override int Parallelism => 1;
/// <inheritdoc/>
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
{

3
src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs

@ -78,9 +78,6 @@ namespace ImageProcessorCore.Processors
/// <inheritdoc/>
public override float[,] KernelY => this.kernelY;
/// <inheritdoc/>
public override int Parallelism => 1;
/// <inheritdoc/>
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
{

2
src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs

@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Creates a glow effect on the image
/// </summary>
public class GlowProcessor : ParallelImageProcessor
public class GlowProcessor : ImageProcessor
{
/// <summary>
/// Gets or sets the glow color to apply.

2
src/ImageProcessorCore/Filters/Processors/InvertProcessor.cs

@ -11,7 +11,7 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// An <see cref="IImageProcessor"/> to invert the colors of an <see cref="Image"/>.
/// </summary>
public class InvertProcessor : ParallelImageProcessor
public class InvertProcessor : ImageProcessor
{
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)

7
src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs

@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// An <see cref="IImageProcessor"/> to invert the colors of an <see cref="Image"/>.
/// </summary>
public class PixelateProcessor : ParallelImageProcessor
public class PixelateProcessor : ImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="PixelateProcessor"/> class.
@ -27,9 +27,6 @@ namespace ImageProcessorCore.Processors
this.Value = size;
}
/// <inheritdoc/>
public override int Parallelism { get; set; } = 1;
/// <summary>
/// Gets or the pixel size.
/// </summary>
@ -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();
}
});

2
src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs

@ -12,7 +12,7 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Creates a vignette effect on the image
/// </summary>
public class VignetteProcessor : ParallelImageProcessor
public class VignetteProcessor : ImageProcessor
{
/// <summary>
/// Gets or sets the vignette color to apply.

70
src/ImageProcessorCore/ParallelImageProcessor.cs → src/ImageProcessorCore/ImageProcessor.cs

@ -1,4 +1,4 @@
// <copyright file="ParallelImageProcessor.cs" company="James Jackson-South">
// <copyright file="ImageProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -7,21 +7,15 @@ namespace ImageProcessorCore.Processors
{
using System;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// Allows the application of processors using parallel processing.
/// Allows the application of processors to images.
/// </summary>
public abstract class ParallelImageProcessor : IImageProcessor
public abstract class ImageProcessor : IImageProcessor
{
/// <inheritdoc/>
public event ProgressEventHandler OnProgress;
/// <summary>
/// Gets or sets the count of workers to run the process in parallel.
/// </summary>
public virtual int Parallelism { get; set; } = Environment.ProcessorCount * 2;
/// <summary>
/// The number of rows processed by a derived class.
/// </summary>
@ -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);
}

22
src/ImageProcessorCore/PixelAccessor.cs

@ -16,7 +16,7 @@ namespace ImageProcessorCore
/// <summary>
/// The position of the first pixel in the bitmap.
/// </summary>
private float* pixelsBase;
private Color* pixelsBase;
/// <summary>
/// Provides a way to access the pixels from unmanaged memory.
@ -48,24 +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)
{
this.pixelsBase = pbuffer;
}
}
this.pixelsHandle = GCHandle.Alloc(image.Pixels, GCHandleType.Pinned);
this.pixelsBase = (Color*)this.pixelsHandle.AddrOfPinnedObject().ToPointer();
}
/// <summary>
@ -113,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
@ -129,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;
}
}

13
src/ImageProcessorCore/Samplers/Processors/CropProcessor.cs

@ -15,8 +15,6 @@ namespace ImageProcessorCore.Processors
/// <inheritdoc/>
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();
});
}
}

2
src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs

@ -92,7 +92,7 @@ namespace ImageProcessorCore.Processors
}
/// <inheritdoc/>
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)

2
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.
/// </summary>
public abstract class ImageSampler : ParallelImageProcessor, IImageSampler
public abstract class ImageSampler : ImageProcessor, IImageSampler
{
/// <inheritdoc/>
public virtual bool Compand { get; set; } = false;

47
src/ImageProcessorCore/Samplers/Processors/Matrix3x2Processor.cs

@ -0,0 +1,47 @@
// <copyright file="Matrix3x2Processor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Processors
{
using System.Numerics;
/// <summary>
/// Provides methods to transform an image using a <see cref="Matrix3x2"/>.
/// </summary>
public abstract class Matrix3x2Processor : ImageSampler
{
/// <summary>
/// Creates a new target to contain the results of the matrix transform.
/// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="sourceRectangle">The source rectangle.</param>
/// <param name="processMatrix">The processing matrix.</param>
protected 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]);
}
}
/// <summary>
/// Gets a transform matrix adjusted to center upon the target image bounds.
/// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image.</param>
/// <param name="matrix">The transform matrix.</param>
/// <returns>
/// The <see cref="Matrix3x2"/>.
/// </returns>
protected static Matrix3x2 GetCenteredMatrix(ImageBase target, ImageBase source, Matrix3x2 matrix)
{
Matrix3x2 translationToTargetCenter = Matrix3x2.CreateTranslation(-target.Width / 2f, -target.Height / 2f);
Matrix3x2 translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width / 2f, source.Height / 2f);
return (translationToTargetCenter * matrix) * translateToSourceCenter;
}
}
}

13
src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs

@ -31,9 +31,6 @@ namespace ImageProcessorCore.Processors
this.Sampler = sampler;
}
/// <inheritdoc/>
public override int Parallelism { get; set; } = 1;
/// <summary>
/// Gets the sampler to perform the resize operation.
/// </summary>
@ -62,13 +59,7 @@ namespace ImageProcessorCore.Processors
}
/// <inheritdoc/>
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)
@ -217,7 +208,7 @@ namespace ImageProcessorCore.Processors
}
/// <inheritdoc/>
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)

3
src/ImageProcessorCore/Samplers/Processors/RotateFlipProcessor.cs

@ -34,9 +34,6 @@ namespace ImageProcessorCore.Processors
/// </summary>
public RotateType RotateType { get; }
/// <inheritdoc/>
public override int Parallelism { get; set; } = 1;
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{

64
src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs

@ -11,85 +11,57 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Provides methods that allow the rotating of images.
/// </summary>
public class RotateProcessor : ImageSampler
public class RotateProcessor : Matrix3x2Processor
{
/// <inheritdoc/>
public override int Parallelism { get; set; } = 1;
/// <summary>
/// Gets or sets the angle of rotation in degrees.
/// The tranform matrix to apply.
/// </summary>
public float Angle { get; set; }
private Matrix3x2 processMatrix;
/// <summary>
/// Gets or sets the center point.
/// Gets or sets the angle of processMatrix in degrees.
/// </summary>
public Point Center { get; set; }
public float Angle { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to expand the canvas to fit the rotated image.
/// </summary>
public bool Expand { get; set; }
public bool Expand { get; set; } = true;
/// <inheritdoc/>
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
{
processMatrix = Point.CreateRotation(new Point(0, 0), -this.Angle);
if (this.Expand)
{
Point centre = this.Center == Point.Empty ? Rectangle.Center(sourceRectangle) : this.Center;
Matrix3x2 rotation = Point.CreateRotation(centre, -this.Angle);
Matrix3x2 invertedRotation;
Matrix3x2.Invert(rotation, out invertedRotation);
Rectangle bounds = ImageMaths.GetBoundingRectangle(source.Bounds, invertedRotation);
target.SetPixels(bounds.Width, bounds.Height, new float[bounds.Width * bounds.Height * 4]);
CreateNewTarget(target, sourceRectangle, processMatrix);
}
}
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
int height = target.Height;
int startX = 0;
int endX = target.Width;
Point centre = this.Center == Point.Empty ? Rectangle.Center(target.Bounds) : this.Center;
//Matrix3x2 invertedRotation;
Matrix3x2 rotation = Point.CreateRotation(centre, -this.Angle);
//Matrix3x2.Invert(rotation, out invertedRotation);
//Vector2 rightTop = Vector2.Transform(new Vector2(source.Width, 0), invertedRotation);
//Vector2 leftBottom = Vector2.Transform(new Vector2(0, source.Height), invertedRotation);
//if (this.Angle < 0)
//{
// rotation = Point.CreateRotation(new Point((int)-leftBottom.X, (int)leftBottom.Y), -this.Angle);
//}
//if (this.Angle > 0)
//{
// rotation = Point.CreateRotation(new Point((int)rightTop.X, (int)-rightTop.Y), -this.Angle);
//}
Matrix3x2 matrix = GetCenteredMatrix(target, source, this.processMatrix);
// Since we are not working in parallel we use full height and width
// of the first pass image.
using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock())
{
Parallel.For(
0,
height,
target.Height,
y =>
{
for (int x = 0; x < target.Width; x++)
{
for (int x = startX; x < endX; x++)
Point transformedPoint = Point.Rotate(new Point(x, y), matrix);
if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y))
{
Point rotated = Point.Rotate(new Point(x, y), rotation);
if (source.Bounds.Contains(rotated.X, rotated.Y))
{
targetPixels[x, y] = sourcePixels[rotated.X, rotated.Y];
}
targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y];
}
}
this.OnRowProcessed();
});
OnRowProcessed();
});
}
}
}

68
src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs

@ -11,10 +11,12 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Provides methods that allow the skewing of images.
/// </summary>
public class SkewProcessor : ImageSampler
public class SkewProcessor : Matrix3x2Processor
{
/// <inheritdoc/>
public override int Parallelism { get; set; } = 1;
/// <summary>
/// The tranform matrix to apply.
/// </summary>
private Matrix3x2 processMatrix;
/// <summary>
/// Gets or sets the angle of rotation along the x-axis in degrees.
@ -26,77 +28,45 @@ namespace ImageProcessorCore.Processors
/// </summary>
public float AngleY { get; set; }
/// <summary>
/// Gets or sets the center point.
/// </summary>
public Point Center { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to expand the canvas to fit the skewed image.
/// </summary>
public bool Expand { get; set; }
public bool Expand { get; set; } = true;
/// <inheritdoc/>
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
{
this.processMatrix = Point.CreateSkew(new Point(0, 0), -this.AngleX, -this.AngleY);
if (this.Expand)
{
Point centre = this.Center;
Matrix3x2 skew = Point.CreateSkew(centre, -this.AngleX, -this.AngleY);
Matrix3x2 invertedSkew;
Matrix3x2.Invert(skew, out invertedSkew);
Rectangle bounds = ImageMaths.GetBoundingRectangle(source.Bounds, invertedSkew);
target.SetPixels(bounds.Width, bounds.Height, new float[bounds.Width * bounds.Height * 4]);
CreateNewTarget(target, sourceRectangle, this.processMatrix);
}
}
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
int height = target.Height;
int startX = 0;
int endX = target.Width;
Point centre = this.Center;
Matrix3x2 invertedSkew;
Matrix3x2 skew = Point.CreateSkew(centre, -this.AngleX, -this.AngleY);
Matrix3x2.Invert(skew, out invertedSkew);
Vector2 rightTop = Vector2.Transform(new Vector2(source.Width, 0), invertedSkew);
Vector2 leftBottom = Vector2.Transform(new Vector2(0, source.Height), invertedSkew);
if (this.AngleX < 0 && this.AngleY > 0)
{
skew = Point.CreateSkew(new Point((int)-leftBottom.X, (int)leftBottom.Y), -this.AngleX, -this.AngleY);
}
if (this.AngleX > 0 && this.AngleY < 0)
{
skew = Point.CreateSkew(new Point((int)rightTop.X, (int)-rightTop.Y), -this.AngleX, -this.AngleY);
}
if (this.AngleX < 0 && this.AngleY < 0)
{
skew = Point.CreateSkew(new Point(target.Width - 1, target.Height - 1), -this.AngleX, -this.AngleY);
}
Matrix3x2 matrix = GetCenteredMatrix(target, source, this.processMatrix);
using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock())
{
Parallel.For(
0,
height,
target.Height,
y =>
{
for (int x = startX; x < endX; x++)
{
Point skewed = Point.Skew(new Point(x, y), skew);
if (source.Bounds.Contains(skewed.X, skewed.Y))
for (int x = 0; x < target.Width; x++)
{
targetPixels[x, y] = sourcePixels[skewed.X, skewed.Y];
Point transformedPoint = Point.Skew(new Point(x, y), matrix);
if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y))
{
targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y];
}
}
}
this.OnRowProcessed();
});
OnRowProcessed();
});
}
}
}

4
src/ImageProcessorCore/Samplers/Resamplers/CatmullRomResampler.cs

@ -6,7 +6,9 @@
namespace ImageProcessorCore
{
/// <summary>
/// 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.
/// <see href="http://www.imagemagick.org/Usage/filter/#cubic_bc"/>
/// </summary>
public class CatmullRomResampler : IResampler

3
src/ImageProcessorCore/Samplers/Resamplers/HermiteResampler.cs

@ -6,7 +6,8 @@
namespace ImageProcessorCore.Processors
{
/// <summary>
/// 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.
/// <see href="http://www.imagemagick.org/Usage/filter/#cubic_bc"/>
/// </summary>
public class HermiteResampler : IResampler

1
src/ImageProcessorCore/Samplers/Resamplers/Lanczos3Resampler.cs

@ -8,6 +8,7 @@ namespace ImageProcessorCore
/// <summary>
/// The function implements the Lanczos kernel algorithm as described on
/// <see href="https://en.wikipedia.org/wiki/Lanczos_resampling#Algorithm">Wikipedia</see>
/// with a radius of 3 pixels.
/// </summary>
public class Lanczos3Resampler : IResampler
{

1
src/ImageProcessorCore/Samplers/Resamplers/Lanczos5Resampler.cs

@ -8,6 +8,7 @@ namespace ImageProcessorCore
/// <summary>
/// The function implements the Lanczos kernel algorithm as described on
/// <see href="https://en.wikipedia.org/wiki/Lanczos_resampling#Algorithm">Wikipedia</see>
/// with a radius of 5 pixels.
/// </summary>
public class Lanczos5Resampler : IResampler
{

1
src/ImageProcessorCore/Samplers/Resamplers/Lanczos8Resampler.cs

@ -8,6 +8,7 @@ namespace ImageProcessorCore
/// <summary>
/// The function implements the Lanczos kernel algorithm as described on
/// <see href="https://en.wikipedia.org/wiki/Lanczos_resampling#Algorithm">Wikipedia</see>
/// with a radius of 8 pixels.
/// </summary>
public class Lanczos8Resampler : IResampler
{

11
src/ImageProcessorCore/Samplers/Rotate.cs

@ -1,7 +1,7 @@
// <copyright file="Rotate.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
// </copyright>
namespace ImageProcessorCore
{
@ -21,21 +21,20 @@ namespace ImageProcessorCore
/// <returns>The <see cref="Image"/></returns>
public static Image Rotate(this Image source, float degrees, ProgressEventHandler progressHandler = null)
{
return Rotate(source, degrees, Point.Empty, true, progressHandler);
return Rotate(source, degrees, true, progressHandler);
}
/// <summary>
/// Rotates an image by the given angle in degrees around the given center point.
/// Rotates an image by the given angle in degrees.
/// </summary>
/// <param name="source">The image to rotate.</param>
/// <param name="degrees">The angle in degrees to perform the rotation.</param>
/// <param name="center">The center point at which to rotate the image.</param>
/// <param name="expand">Whether to expand the image to fit the rotated result.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/></returns>
public static Image Rotate(this Image source, float degrees, Point center, bool expand, ProgressEventHandler progressHandler = null)
public static Image Rotate(this Image source, float degrees, 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

11
src/ImageProcessorCore/Samplers/Skew.cs

@ -1,7 +1,7 @@
// <copyright file="Skew.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
// </copyright>
namespace ImageProcessorCore
{
@ -22,22 +22,21 @@ namespace ImageProcessorCore
/// <returns>The <see cref="Image"/></returns>
public static Image Skew(this Image source, float degreesX, float degreesY, ProgressEventHandler progressHandler = null)
{
return Skew(source, degreesX, degreesY, Point.Empty, true, progressHandler);
return Skew(source, degreesX, degreesY, true, progressHandler);
}
/// <summary>
/// Skews an image by the given angles in degrees around the given center point.
/// Skews an image by the given angles in degrees.
/// </summary>
/// <param name="source">The image to skew.</param>
/// <param name="degreesX">The angle in degrees to perform the rotation along the x-axis.</param>
/// <param name="degreesY">The angle in degrees to perform the rotation along the y-axis.</param>
/// <param name="center">The center point at which to skew the image.</param>
/// <param name="expand">Whether to expand the image to fit the skewed result.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/></returns>
public static Image Skew(this Image source, float degreesX, float degreesY, Point center, bool expand, ProgressEventHandler progressHandler = null)
public static Image Skew(this Image source, float degreesX, float degreesY, bool expand, ProgressEventHandler progressHandler = null)
{
SkewProcessor processor = new SkewProcessor { AngleX = degreesX, AngleY = degreesY, Center = center, Expand = expand };
SkewProcessor processor = new SkewProcessor { AngleX = degreesX, AngleY = degreesY, Expand = expand };
processor.OnProgress += progressHandler;
try

4
tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs

@ -429,7 +429,7 @@ namespace ImageProcessorCore.Tests
Image image = new Image(stream);
using (FileStream output = File.OpenWrite($"TestOutput/Rotate/{filename}"))
{
image.Rotate(63, this.ProgressUpdate)
image.Rotate(-170, this.ProgressUpdate)
.Save(output);
}
}
@ -454,7 +454,7 @@ namespace ImageProcessorCore.Tests
Image image = new Image(stream);
using (FileStream output = File.OpenWrite($"TestOutput/Skew/{filename}"))
{
image.Skew(-15, 12, this.ProgressUpdate)
image.Skew(-20, -10, this.ProgressUpdate)
.Save(output);
}
}

Loading…
Cancel
Save