Browse Source

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

# Conflicts:
#	src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs


Former-commit-id: 26e39be6bf1ecab2e27355bd6410856aec4602e5
Former-commit-id: 5b72abf1b293d733850a5c68a0938cd123044a32
Former-commit-id: 20931e70d827c18877d1f5eec290258be6c14fd2
pull/1/head
James Jackson-South 10 years ago
parent
commit
81d60cdfd9
  1. 5
      src/ImageProcessorCore/Bootstrapper.cs
  2. 65
      src/ImageProcessorCore/Colors/Color.cs
  3. 7
      src/ImageProcessorCore/Colors/Colorspaces/IAlmostEquatable.cs
  4. 17
      src/ImageProcessorCore/Colors/PackedVector/IPackedVector.cs
  5. 2
      src/ImageProcessorCore/Common/Extensions/ByteExtensions.cs
  6. 97
      src/ImageProcessorCore/Common/Helpers/Guard.cs
  7. 6
      src/ImageProcessorCore/Filters/Blend.cs
  8. 8
      src/ImageProcessorCore/Filters/BoxBlur.cs
  9. 2
      src/ImageProcessorCore/Filters/DetectEdges.cs
  10. 8
      src/ImageProcessorCore/Filters/Invert.cs
  11. 2
      src/ImageProcessorCore/Filters/Kodachrome.cs
  12. 6
      src/ImageProcessorCore/Filters/Pixelate.cs
  13. 2
      src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs
  14. 2
      src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs
  15. 4
      src/ImageProcessorCore/Filters/Processors/Binarization/BinaryThresholdProcessor.cs
  16. 2
      src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs
  17. 2
      src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs
  18. 13
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/HueProcessor.cs
  19. 2
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/IColorMatrixFilter.cs
  20. 2
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs
  21. 24
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/SaturationProcessor.cs
  22. 2
      src/ImageProcessorCore/Filters/Processors/ColorMatrix/SepiaProcessor.cs
  23. 8
      src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs
  24. 2
      src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs
  25. 2
      src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs
  26. 2
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetector2DFilter.cs
  27. 2
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetectorFilter.cs
  28. 2
      src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs
  29. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs
  30. 6
      src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs
  31. 6
      src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs
  32. 4
      src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs
  33. 3
      src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs
  34. 2
      src/ImageProcessorCore/Filters/Sepia.cs
  35. 4
      src/ImageProcessorCore/Formats/Bmp/BmpDecoder.cs
  36. 11
      src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs
  37. 13
      src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs
  38. 2
      src/ImageProcessorCore/Formats/Gif/GifDecoderCore.cs
  39. 1
      src/ImageProcessorCore/Formats/Gif/LzwDecoder.cs
  40. 2
      src/ImageProcessorCore/Formats/Gif/LzwEncoder.cs
  41. 2
      src/ImageProcessorCore/Formats/Gif/Sections/GifGraphicsControlExtension.cs
  42. 4
      src/ImageProcessorCore/Formats/IImageEncoder.cs
  43. 2
      src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id
  44. 10
      src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs
  45. 2
      src/ImageProcessorCore/Formats/Png/Filters/UpFilter.cs
  46. 2
      src/ImageProcessorCore/Formats/Png/PngDecoder.cs
  47. 13
      src/ImageProcessorCore/Formats/Png/PngDecoderCore.cs
  48. 4
      src/ImageProcessorCore/Formats/Png/PngEncoder.cs
  49. 2
      src/ImageProcessorCore/IO/EndianBitConverter.cs
  50. 12
      src/ImageProcessorCore/Image/IImageBase.cs
  51. 4
      src/ImageProcessorCore/Image/IImageProcessor.cs
  52. 53
      src/ImageProcessorCore/Image/ImageBase.cs
  53. 20
      src/ImageProcessorCore/Image/ImageExtensions.cs
  54. 26
      src/ImageProcessorCore/Image/PixelAccessor.cs
  55. 4
      src/ImageProcessorCore/ImageProcessor.cs
  56. 219
      src/ImageProcessorCore/Numerics/BigRational.cs
  57. 545
      src/ImageProcessorCore/Numerics/Rational.cs
  58. 199
      src/ImageProcessorCore/Numerics/SignedRational.cs
  59. 2
      src/ImageProcessorCore/Profiles/Exif/ExifProfile.cs
  60. 10
      src/ImageProcessorCore/Profiles/Exif/ExifReader.cs
  61. 10
      src/ImageProcessorCore/Profiles/Exif/ExifValue.cs
  62. 13
      src/ImageProcessorCore/Profiles/Exif/ExifWriter.cs
  63. 2
      src/ImageProcessorCore/Quantizers/IQuantizer.cs
  64. 24
      src/ImageProcessorCore/Quantizers/Octree/OctreeQuantizer.cs
  65. 6
      src/ImageProcessorCore/Quantizers/Octree/Quantizer.cs
  66. 4
      src/ImageProcessorCore/Quantizers/Quantize.cs
  67. 4
      src/ImageProcessorCore/Quantizers/QuantizedImage.cs
  68. 2
      src/ImageProcessorCore/Quantizers/Wu/WuQuantizer.cs
  69. 31
      src/ImageProcessorCore/Samplers/AutoOrient.cs
  70. 18
      src/ImageProcessorCore/Samplers/Options/ResizeHelper.cs
  71. 4
      src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs
  72. 2
      src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs
  73. 16
      src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs
  74. 2
      src/ImageProcessorCore/Samplers/Resamplers/NearestNeighborResampler.cs
  75. 4
      src/ImageProcessorCore/Samplers/RotateFlip.cs
  76. 2
      tests/ImageProcessorCore.Benchmarks/General/ArrayCopy.cs
  77. 57
      tests/ImageProcessorCore.Benchmarks/Image/CopyPixels.cs
  78. 4
      tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs
  79. 8
      tests/ImageProcessorCore.Tests/Helpers/GuardTests.cs
  80. 64
      tests/ImageProcessorCore.Tests/Numerics/RationalTests.cs
  81. 122
      tests/ImageProcessorCore.Tests/Numerics/SignedRationalTests.cs
  82. 110
      tests/ImageProcessorCore.Tests/Profiles/Exif/ExifProfileTests.cs

5
src/ImageProcessorCore/Bootstrapper.cs

@ -8,7 +8,6 @@ namespace ImageProcessorCore
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Formats; using Formats;
@ -51,8 +50,8 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Gets the collection of supported <see cref="IImageFormat"/> /// Gets the collection of supported <see cref="IImageFormat"/>
/// </summary> /// </summary>
public IReadOnlyCollection<IImageFormat> ImageFormats => public IReadOnlyCollection<IImageFormat> ImageFormats => new ReadOnlyCollection<IImageFormat>(this.imageFormats);
new ReadOnlyCollection<IImageFormat>(this.imageFormats);
/// <summary> /// <summary>
/// Gets or sets the global parallel options for processing tasks in parallel. /// Gets or sets the global parallel options for processing tasks in parallel.

65
src/ImageProcessorCore/Colors/Color.cs

@ -19,8 +19,24 @@ namespace ImageProcessorCore
/// </remarks> /// </remarks>
public partial struct Color : IPackedVector<uint>, IEquatable<Color> public partial struct Color : IPackedVector<uint>, IEquatable<Color>
{ {
private const float Max = 255F; /// <summary>
private const float Min = 0F; /// The maximum byte value
/// </summary>
private const float MaxBytes = 255F;
/// <summary>
/// The minimum vector value
/// </summary>
private const float Zero = 0F;
/// <summary>
/// The maximum vector value
/// </summary>
private const float One = 1F;
/// <summary>
/// The packed value
/// </summary>
private uint packedValue; private uint packedValue;
/// <summary> /// <summary>
@ -34,6 +50,7 @@ namespace ImageProcessorCore
} }
set set
{ {
// AABBGGRR
this.packedValue = (uint)(this.packedValue & -0x100 | value); this.packedValue = (uint)(this.packedValue & -0x100 | value);
} }
} }
@ -50,7 +67,8 @@ namespace ImageProcessorCore
} }
set set
{ {
this.packedValue = (uint)(this.packedValue & -0xff01 | (uint)value << 8); // AABBGGRR
this.packedValue = (uint)(this.packedValue & -0xFF01 | (uint)value << 8);
} }
} }
@ -65,7 +83,8 @@ namespace ImageProcessorCore
} }
set set
{ {
this.packedValue = (uint)(this.packedValue & -0xff0001 | (uint)(value << 16)); // AABBGGRR
this.packedValue = (uint)(this.packedValue & -0xFF0001 | (uint)(value << 16));
} }
} }
@ -80,7 +99,8 @@ namespace ImageProcessorCore
} }
set set
{ {
this.packedValue = this.packedValue & 0xffffff | (uint)value << 24; // AABBGGRR
this.packedValue = this.packedValue & 0xFFFFFF | (uint)value << 24;
} }
} }
@ -217,40 +237,16 @@ namespace ImageProcessorCore
return left.packedValue != right.packedValue; return left.packedValue != right.packedValue;
} }
/// <inheritdoc/>
public uint GetPackedValue()
{
return this.packedValue;
}
/// <inheritdoc/>
public void SetPackedValue(uint value)
{
this.packedValue = value;
}
/// <inheritdoc/> /// <inheritdoc/>
public void PackFromVector4(Vector4 vector) public void PackFromVector4(Vector4 vector)
{ {
this.packedValue = Pack(ref vector); this.packedValue = Pack(ref vector);
} }
/// <inheritdoc/>
public void PackFromBytes(byte x, byte y, byte z, byte w)
{
this.packedValue = (uint)(x | y << 8 | z << 16 | w << 24);
}
/// <inheritdoc/> /// <inheritdoc/>
public Vector4 ToVector4() public Vector4 ToVector4()
{ {
return new Vector4(this.R, this.G, this.B, this.A) / 255F; return new Vector4(this.R, this.G, this.B, this.A) / MaxBytes;
}
/// <inheritdoc/>
public byte[] ToBytes()
{
return new[] { this.R, this.G, this.B, this.A };
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -308,12 +304,13 @@ namespace ImageProcessorCore
/// <param name="z">The z-component</param> /// <param name="z">The z-component</param>
/// <param name="w">The w-component</param> /// <param name="w">The w-component</param>
/// <returns>The <see cref="uint"/></returns> /// <returns>The <see cref="uint"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint Pack(float x, float y, float z, float w) private static uint Pack(float x, float y, float z, float w)
{ {
return (uint)((byte)Math.Round(x.Clamp(0, 1) * Max) return (uint)((byte)Math.Round(x.Clamp(Zero, One) * MaxBytes)
| ((byte)Math.Round(y.Clamp(0, 1) * Max) << 8) | ((byte)Math.Round(y.Clamp(Zero, One) * MaxBytes) << 8)
| (byte)Math.Round(z.Clamp(0, 1) * Max) << 16 | (byte)Math.Round(z.Clamp(Zero, One) * MaxBytes) << 16
| (byte)Math.Round(w.Clamp(0, 1) * Max) << 24); | (byte)Math.Round(w.Clamp(Zero, One) * MaxBytes) << 24);
} }
/// <summary> /// <summary>

7
src/ImageProcessorCore/Colors/Colorspaces/IAlmostEquatable.cs

@ -12,8 +12,9 @@ namespace ImageProcessorCore
/// a type-specific method for determining approximate equality of instances. /// a type-specific method for determining approximate equality of instances.
/// </summary> /// </summary>
/// <typeparam name="TColor">The type of objects to compare.</typeparam> /// <typeparam name="TColor">The type of objects to compare.</typeparam>
/// <typeparam name="TPacked">The object specifying the type to specify precision with.</typeparam> /// <typeparam name="TPrecision">The object specifying the type to specify precision with.</typeparam>
public interface IAlmostEquatable<TColor, TPacked> where TPacked : struct, IComparable<TPacked> public interface IAlmostEquatable<in TColor, in TPrecision>
where TPrecision : struct, IComparable<TPrecision>
{ {
/// <summary> /// <summary>
/// Indicates whether the current object is equal to another object of the same type /// Indicates whether the current object is equal to another object of the same type
@ -24,6 +25,6 @@ namespace ImageProcessorCore
/// <returns> /// <returns>
/// true if the current object is equal to the other parameter; otherwise, false. /// true if the current object is equal to the other parameter; otherwise, false.
/// </returns> /// </returns>
bool AlmostEquals(TColor other, TPacked precision); bool AlmostEquals(TColor other, TPrecision precision);
} }
} }

17
src/ImageProcessorCore/Colors/PackedVector/IPackedVector.cs

@ -29,28 +29,11 @@ namespace ImageProcessorCore
/// <param name="vector">The vector to create the packed representation from.</param> /// <param name="vector">The vector to create the packed representation from.</param>
void PackFromVector4(Vector4 vector); void PackFromVector4(Vector4 vector);
///// <summary>
///// Sets the packed representation from a <see cref="T:byte[]"/>.
///// </summary>
///// <param name="x">The x-component to create the packed representation from.</param>
///// <param name="y">The y-component to create the packed representation from.</param>
///// <param name="z">The z-component to create the packed representation from.</param>
///// <param name="w">The w-component to create the packed representation from.</param>
//void PackFromBytes(byte x, byte y, byte z, byte w);
/// <summary> /// <summary>
/// Expands the packed representation into a <see cref="Vector4"/>. /// Expands the packed representation into a <see cref="Vector4"/>.
/// The vector components are typically expanded in least to greatest significance order. /// The vector components are typically expanded in least to greatest significance order.
/// </summary> /// </summary>
/// <returns>The <see cref="Vector4"/>.</returns> /// <returns>The <see cref="Vector4"/>.</returns>
Vector4 ToVector4(); Vector4 ToVector4();
///// <summary>
///// Expands the packed representation into a <see cref="T:byte[]"/>.
///// The bytes are typically expanded in least to greatest significance order.
///// Red -> Green -> Blue -> Alpha
///// </summary>
///// <returns>The <see cref="Vector4"/>.</returns>
//byte[] ToBytes();
} }
} }

2
src/ImageProcessorCore/Common/Extensions/ByteExtensions.cs

@ -20,7 +20,7 @@ namespace ImageProcessorCore
/// <param name="bits">The number of bits per value.</param> /// <param name="bits">The number of bits per value.</param>
/// <returns>The resulting <see cref="T:byte[]"/> array. Is never null.</returns> /// <returns>The resulting <see cref="T:byte[]"/> array. Is never null.</returns>
/// <exception cref="System.ArgumentNullException"><paramref name="bytes"/> is null.</exception> /// <exception cref="System.ArgumentNullException"><paramref name="bytes"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="bits"/> is less than or equals than zero.</exception> /// <exception cref="System.ArgumentException"><paramref name="bits"/> is less than or equals than zero.</exception>
public static byte[] ToArrayByBitsLength(this byte[] bytes, int bits) public static byte[] ToArrayByBitsLength(this byte[] bytes, int bits)
{ {
Guard.NotNull(bytes, "bytes"); Guard.NotNull(bytes, "bytes");

97
src/ImageProcessorCore/Common/Helpers/Guard.cs

@ -1,16 +1,8 @@
// -------------------------------------------------------------------------------------------------------------------- // <copyright file="Guard.cs" company="James Jackson-South">
// <copyright file="Guard.cs" company="James Jackson-South"> // Copyright (c) James Jackson-South and contributors.
// Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0.
// Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
// <summary>
// Provides methods to protect against invalid parameters.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("ImageProcessorCore.Tests")]
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using System; using System;
@ -26,23 +18,15 @@ namespace ImageProcessorCore
/// Verifies, that the method parameter with specified object value is not null /// Verifies, that the method parameter with specified object value is not null
/// and throws an exception if it is found to be so. /// and throws an exception if it is found to be so.
/// </summary> /// </summary>
/// <param name="target"> /// <param name="target">The target object, which cannot be null.</param>
/// The target object, which cannot be null. /// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// </param> /// <param name="message">The error message, if any to add to the exception.</param>
/// <param name="parameterName"> /// <exception cref="ArgumentNullException"><paramref name="target"/> is null</exception>
/// The name of the parameter that is to be checked.
/// </param>
/// <param name="message">
/// The error message, if any to add to the exception.
/// </param>
/// <exception cref="System.ArgumentNullException">
/// <paramref name="target"/> is null
/// </exception>
public static void NotNull(object target, string parameterName, string message = "") public static void NotNull(object target, string parameterName, string message = "")
{ {
if (target == null) if (target == null)
{ {
if (string.IsNullOrWhiteSpace(message)) if (!string.IsNullOrWhiteSpace(message))
{ {
throw new ArgumentNullException(parameterName, message); throw new ArgumentNullException(parameterName, message);
} }
@ -58,13 +42,8 @@ namespace ImageProcessorCore
/// </summary> /// </summary>
/// <param name="target">The target string, which should be checked against being null or empty.</param> /// <param name="target">The target string, which should be checked against being null or empty.</param>
/// <param name="parameterName">Name of the parameter.</param> /// <param name="parameterName">Name of the parameter.</param>
/// <exception cref="System.ArgumentNullException"> /// <exception cref="ArgumentNullException"><paramref name="target"/> is null.</exception>
/// <paramref name="target"/> is null. /// <exception cref="ArgumentException"><paramref name="target"/> is empty or contains only blanks.</exception>
/// </exception>
/// <exception cref="ArgumentException">
/// <paramref name="target"/> is
/// empty or contains only blanks.
/// </exception>
public static void NotNullOrEmpty(string target, string parameterName) public static void NotNullOrEmpty(string target, string parameterName)
{ {
if (target == null) if (target == null)
@ -94,9 +73,7 @@ namespace ImageProcessorCore
{ {
if (value.CompareTo(max) >= 0) if (value.CompareTo(max) >= 0)
{ {
throw new ArgumentOutOfRangeException( throw new ArgumentOutOfRangeException(parameterName, $"Value must be less than {max}.");
parameterName,
$"Value must be less than {max}.");
} }
} }
@ -116,9 +93,7 @@ namespace ImageProcessorCore
{ {
if (value.CompareTo(max) > 0) if (value.CompareTo(max) > 0)
{ {
throw new ArgumentOutOfRangeException( throw new ArgumentOutOfRangeException(parameterName, $"Value must be less than or equal to {max}.");
parameterName,
$"Value must be less than or equal to {max}.");
} }
} }
@ -134,7 +109,7 @@ namespace ImageProcessorCore
/// <paramref name="value"/> is less than the minimum value. /// <paramref name="value"/> is less than the minimum value.
/// </exception> /// </exception>
public static void MustBeGreaterThan<TValue>(TValue value, TValue min, string parameterName) public static void MustBeGreaterThan<TValue>(TValue value, TValue min, string parameterName)
where TValue : IComparable<TValue> where TValue : IComparable<TValue>
{ {
if (value.CompareTo(min) <= 0) if (value.CompareTo(min) <= 0)
{ {
@ -156,13 +131,11 @@ namespace ImageProcessorCore
/// <paramref name="value"/> is less than the minimum value. /// <paramref name="value"/> is less than the minimum value.
/// </exception> /// </exception>
public static void MustBeGreaterThanOrEqualTo<TValue>(TValue value, TValue min, string parameterName) public static void MustBeGreaterThanOrEqualTo<TValue>(TValue value, TValue min, string parameterName)
where TValue : IComparable<TValue> where TValue : IComparable<TValue>
{ {
if (value.CompareTo(min) < 0) if (value.CompareTo(min) < 0)
{ {
throw new ArgumentOutOfRangeException( throw new ArgumentOutOfRangeException(parameterName, $"Value must be greater than or equal to {min}.");
parameterName,
$"Value must be greater than or equal to {min}.");
} }
} }
@ -179,13 +152,11 @@ namespace ImageProcessorCore
/// <paramref name="value"/> is less than the minimum value of greater than the maximum value. /// <paramref name="value"/> is less than the minimum value of greater than the maximum value.
/// </exception> /// </exception>
public static void MustBeBetweenOrEqualTo<TValue>(TValue value, TValue min, TValue max, string parameterName) public static void MustBeBetweenOrEqualTo<TValue>(TValue value, TValue min, TValue max, string parameterName)
where TValue : IComparable<TValue> where TValue : IComparable<TValue>
{ {
if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0) if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0)
{ {
throw new ArgumentOutOfRangeException( throw new ArgumentOutOfRangeException(parameterName, $"Value must be greater than or equal to {min} and less than or equal to {max}.");
parameterName,
$"Value must be greater than or equal to {min} and less than or equal to {max}.");
} }
} }
@ -203,18 +174,13 @@ namespace ImageProcessorCore
/// The error message, if any to add to the exception. /// The error message, if any to add to the exception.
/// </param> /// </param>
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="target"/> is null /// <paramref name="target"/> is false
/// </exception> /// </exception>
public static void IsTrue(bool target, string parameterName, string message = "") public static void IsTrue(bool target, string parameterName, string message)
{ {
if (!target) if (!target)
{ {
if (string.IsNullOrWhiteSpace(message)) throw new ArgumentException(message, parameterName);
{
throw new ArgumentException(parameterName, message);
}
throw new ArgumentException(parameterName);
} }
} }
@ -222,28 +188,17 @@ namespace ImageProcessorCore
/// Verifies, that the method parameter with specified target value is false /// Verifies, that the method parameter with specified target value is false
/// and throws an exception if it is found to be so. /// and throws an exception if it is found to be so.
/// </summary> /// </summary>
/// <param name="target"> /// <param name="target">The target value, which cannot be true.</param>
/// The target value, which cannot be true. /// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// </param> /// <param name="message">The error message, if any to add to the exception.</param>
/// <param name="parameterName">
/// The name of the parameter that is to be checked.
/// </param>
/// <param name="message">
/// The error message, if any to add to the exception.
/// </param>
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="target"/> is null /// <paramref name="target"/> is true
/// </exception> /// </exception>
public static void IsFalse(bool target, string parameterName, string message = "") public static void IsFalse(bool target, string parameterName, string message)
{ {
if (target) if (target)
{ {
if (string.IsNullOrWhiteSpace(message)) throw new ArgumentException(message, parameterName);
{
throw new ArgumentException(parameterName, message);
}
throw new ArgumentException(parameterName);
} }
} }
} }

6
src/ImageProcessorCore/Filters/Blend.cs

@ -1,7 +1,7 @@
// <copyright file="Blend.cs" company="James Jackson-South"> // <copyright file="Blend.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
@ -15,10 +15,10 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Combines the given image together with the current one by blending their pixels. /// Combines the given image together with the current one by blending their pixels.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param> /// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image{TColor, TPacked}"/>.</returns> /// <returns>The <see cref="Image{TColor, TPacked}"/>.</returns>

8
src/ImageProcessorCore/Filters/BoxBlur.cs

@ -15,8 +15,8 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Applies a box blur to the image. /// Applies a box blur to the image.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="radius">The 'radius' value representing the size of the area to sample.</param> /// <param name="radius">The 'radius' value representing the size of the area to sample.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
@ -31,8 +31,8 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Applies a box blur to the image. /// Applies a box blur to the image.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="radius">The 'radius' value representing the size of the area to sample.</param> /// <param name="radius">The 'radius' value representing the size of the area to sample.</param>
/// <param name="rectangle"> /// <param name="rectangle">

2
src/ImageProcessorCore/Filters/DetectEdges.cs

@ -29,7 +29,7 @@ namespace ImageProcessorCore
} }
/// <summary> /// <summary>
/// Detects any edges within the image. Uses the <see cref="SobelProcessor{TColor, TPacked}"/> filter /// Detects any edges within the image. Uses the <see cref="SobelProcessor{T,TP}"/> filter
/// operating in Grayscale mode. /// operating in Grayscale mode.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>

8
src/ImageProcessorCore/Filters/Invert.cs

@ -1,20 +1,22 @@
// <copyright file="Invert.cs" company="James Jackson-South"> // <copyright file="Invert.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using Processors; using Processors;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image"/> type. /// Extension methods for the <see cref="Image{TColor, TPacked}"/> type.
/// </summary> /// </summary>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>
/// Inverts the colors of the image. /// Inverts the colors of the image.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
@ -28,6 +30,8 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Inverts the colors of the image. /// Inverts the colors of the image.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.

2
src/ImageProcessorCore/Filters/Kodachrome.cs

@ -10,6 +10,8 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image{TColor, TPacked}"/> type. /// Extension methods for the <see cref="Image{TColor, TPacked}"/> type.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>

6
src/ImageProcessorCore/Filters/Pixelate.cs

@ -11,13 +11,13 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image{TColor, TPacked}"/> type. /// Extension methods for the <see cref="Image{TColor, TPacked}"/> type.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public static partial class ImageExtensions public static partial class ImageExtensions
{ {
/// <summary> /// <summary>
/// Pixelates and image with the given pixel size. /// Pixelates and image with the given pixel size.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="size">The size of the pixels.</param> /// <param name="size">The size of the pixels.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param> /// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
@ -32,6 +32,8 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Pixelates and image with the given pixel size. /// Pixelates and image with the given pixel size.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="size">The size of the pixels.</param> /// <param name="size">The size of the pixels.</param>
/// <param name="rectangle"> /// <param name="rectangle">

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

@ -22,7 +22,7 @@ namespace ImageProcessorCore.Processors
/// Initializes a new instance of the <see cref="AlphaProcessor{TColor, TPacked}"/> class. /// Initializes a new instance of the <see cref="AlphaProcessor{TColor, TPacked}"/> class.
/// </summary> /// </summary>
/// <param name="percent">The percentage to adjust the opacity of the image. Must be between 0 and 100.</param> /// <param name="percent">The percentage to adjust the opacity of the image. Must be between 0 and 100.</param>
/// <exception cref="ArgumentException"> /// <exception cref="System.ArgumentException">
/// <paramref name="percent"/> is less than 0 or is greater than 100. /// <paramref name="percent"/> is less than 0 or is greater than 100.
/// </exception> /// </exception>
public AlphaProcessor(int percent) public AlphaProcessor(int percent)

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

@ -24,7 +24,7 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BackgroundColorProcessor{TColor, TPacked}"/> class. /// Initializes a new instance of the <see cref="BackgroundColorProcessor{TColor, TPacked}"/> class.
/// </summary> /// </summary>
/// <param name="color">The <see cref="T"/> to set the background color to.</param> /// <param name="color">The <see cref="TColor"/> to set the background color to.</param>
public BackgroundColorProcessor(TColor color) public BackgroundColorProcessor(TColor color)
{ {
this.Value = color; this.Value = color;

4
src/ImageProcessorCore/Filters/Processors/Binarization/BinaryThresholdProcessor.cs

@ -22,7 +22,7 @@ namespace ImageProcessorCore.Processors
/// Initializes a new instance of the <see cref="BinaryThresholdProcessor{TColor, TPacked}"/> class. /// Initializes a new instance of the <see cref="BinaryThresholdProcessor{TColor, TPacked}"/> class.
/// </summary> /// </summary>
/// <param name="threshold">The threshold to split the image. Must be between 0 and 1.</param> /// <param name="threshold">The threshold to split the image. Must be between 0 and 1.</param>
/// <exception cref="ArgumentException"> /// <exception cref="System.ArgumentException">
/// <paramref name="threshold"/> is less than 0 or is greater than 1. /// <paramref name="threshold"/> is less than 0 or is greater than 1.
/// </exception> /// </exception>
public BinaryThresholdProcessor(float threshold) public BinaryThresholdProcessor(float threshold)
@ -111,4 +111,4 @@ namespace ImageProcessorCore.Processors
} }
} }
} }
} }

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

@ -24,7 +24,7 @@ namespace ImageProcessorCore.Processors
private readonly ImageBase<TColor, TPacked> blend; private readonly ImageBase<TColor, TPacked> blend;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BlendProcessor{TColor, TPacked}"/> class. /// Initializes a new instance of the <see cref="BlendProcessor{T,TP}"/> class.
/// </summary> /// </summary>
/// <param name="image"> /// <param name="image">
/// The image to blend with the currently processing image. /// The image to blend with the currently processing image.

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

@ -22,7 +22,7 @@ namespace ImageProcessorCore.Processors
/// Initializes a new instance of the <see cref="BrightnessProcessor{TColor, TPacked}"/> class. /// Initializes a new instance of the <see cref="BrightnessProcessor{TColor, TPacked}"/> class.
/// </summary> /// </summary>
/// <param name="brightness">The new brightness of the image. Must be between -100 and 100.</param> /// <param name="brightness">The new brightness of the image. Must be between -100 and 100.</param>
/// <exception cref="ArgumentException"> /// <exception cref="System.ArgumentException">
/// <paramref name="brightness"/> is less than -100 or is greater than 100. /// <paramref name="brightness"/> is less than -100 or is greater than 100.
/// </exception> /// </exception>
public BrightnessProcessor(int brightness) public BrightnessProcessor(int brightness)

13
src/ImageProcessorCore/Filters/Processors/ColorMatrix/HueProcessor.cs

@ -9,7 +9,7 @@ namespace ImageProcessorCore.Processors
using System.Numerics; using System.Numerics;
/// <summary> /// <summary>
/// An <see cref="IImageProcessor{TColor, TPacked}"/> to change the hue of an <see cref="Image"/>. /// An <see cref="IImageProcessor{TColor, TPacked}"/> to change the hue of an <see cref="Image{TColor, TPacked}"/>.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
@ -18,12 +18,7 @@ namespace ImageProcessorCore.Processors
where TPacked : struct where TPacked : struct
{ {
/// <summary> /// <summary>
/// The <see cref="Matrix4x4"/> used to alter the image. /// Initializes a new instance of the <see cref="HueProcessor{TColor, TPacked}"/> class.
/// </summary>
private Matrix4x4 matrix;
/// <summary>
/// Initializes a new instance of the <see cref="HueProcessor"/> class.
/// </summary> /// </summary>
/// <param name="angle">The new brightness of the image. Must be between -100 and 100.</param> /// <param name="angle">The new brightness of the image. Must be between -100 and 100.</param>
public HueProcessor(float angle) public HueProcessor(float angle)
@ -67,7 +62,7 @@ namespace ImageProcessorCore.Processors
M33 = (float)(lumB + (cosradians * oneMinusLumB) + (sinradians * lumB)) M33 = (float)(lumB + (cosradians * oneMinusLumB) + (sinradians * lumB))
}; };
this.matrix = matrix4X4; this.Matrix = matrix4X4;
} }
/// <summary> /// <summary>
@ -76,7 +71,7 @@ namespace ImageProcessorCore.Processors
public float Angle { get; } public float Angle { get; }
/// <inheritdoc/> /// <inheritdoc/>
public override Matrix4x4 Matrix => this.matrix; public override Matrix4x4 Matrix { get; }
/// <inheritdoc/> /// <inheritdoc/>
public override bool Compand => false; public override bool Compand => false;

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

@ -11,6 +11,8 @@ namespace ImageProcessorCore.Processors
/// Encapsulates properties and methods for creating processors that utilize a matrix to /// Encapsulates properties and methods for creating processors that utilize a matrix to
/// alter the image pixels. /// alter the image pixels.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public interface IColorMatrixFilter<TColor, TPacked> : IImageProcessor<TColor, TPacked> public interface IColorMatrixFilter<TColor, TPacked> : IImageProcessor<TColor, TPacked>
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct

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

@ -45,4 +45,4 @@ namespace ImageProcessorCore.Processors
new GlowProcessor<TColor, TPacked> { GlowColor = packedG, Radius = target.Width / 4F }.Apply(target, target, sourceRectangle); new GlowProcessor<TColor, TPacked> { GlowColor = packedG, Radius = target.Width / 4F }.Apply(target, target, sourceRectangle);
} }
} }
} }

24
src/ImageProcessorCore/Filters/Processors/ColorMatrix/SaturationProcessor.cs

@ -8,7 +8,7 @@ namespace ImageProcessorCore.Processors
using System.Numerics; using System.Numerics;
/// <summary> /// <summary>
/// An <see cref="IImageProcessor{TColor, TPacked}"/> to change the saturation of an <see cref="Image"/>. /// An <see cref="IImageProcessor{TColor, TPacked}"/> to change the saturation of an <see cref="Image{TColor, TPacked}"/>.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
@ -17,28 +17,16 @@ namespace ImageProcessorCore.Processors
where TPacked : struct where TPacked : struct
{ {
/// <summary> /// <summary>
/// The saturation to be applied to the image. /// Initializes a new instance of the <see cref="SaturationProcessor{TColor, TPacked}"/> class.
/// </summary>
private readonly int saturation;
/// <summary>
/// The <see cref="Matrix4x4"/> used to alter the image.
/// </summary>
private Matrix4x4 matrix;
/// <summary>
/// Initializes a new instance of the <see cref="SaturationProcessor"/> class.
/// </summary> /// </summary>
/// <param name="saturation">The new saturation of the image. Must be between -100 and 100.</param> /// <param name="saturation">The new saturation of the image. Must be between -100 and 100.</param>
/// <exception cref="ArgumentException"> /// <exception cref="System.ArgumentException">
/// <paramref name="saturation"/> is less than -100 or is greater than 100. /// <paramref name="saturation"/> is less than -100 or is greater than 100.
/// </exception> /// </exception>
public SaturationProcessor(int saturation) public SaturationProcessor(int saturation)
{ {
Guard.MustBeBetweenOrEqualTo(saturation, -100, 100, nameof(saturation)); Guard.MustBeBetweenOrEqualTo(saturation, -100, 100, nameof(saturation));
this.saturation = saturation; float saturationFactor = saturation / 100f;
float saturationFactor = this.saturation / 100f;
// Stop at -1 to prevent inversion. // Stop at -1 to prevent inversion.
saturationFactor++; saturationFactor++;
@ -65,10 +53,10 @@ namespace ImageProcessorCore.Processors
M33 = saturationComplementB + saturationFactor, M33 = saturationComplementB + saturationFactor,
}; };
this.matrix = matrix4X4; this.Matrix = matrix4X4;
} }
/// <inheritdoc/> /// <inheritdoc/>
public override Matrix4x4 Matrix => this.matrix; public override Matrix4x4 Matrix { get; }
} }
} }

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

@ -34,4 +34,4 @@ namespace ImageProcessorCore.Processors
/// <inheritdoc/> /// <inheritdoc/>
public override bool Compand => false; public override bool Compand => false;
} }
} }

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

@ -12,17 +12,17 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// An <see cref="IImageProcessor{TColor, TPacked}"/> to change the contrast of an <see cref="Image{TColor, TPacked}"/>. /// An <see cref="IImageProcessor{TColor, TPacked}"/> to change the contrast of an <see cref="Image{TColor, TPacked}"/>.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>long, float.</example></typeparam>
public class ContrastProcessor<TColor, TPacked> : ImageProcessor<TColor, TPacked> public class ContrastProcessor<TColor, TPacked> : ImageProcessor<TColor, TPacked>
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ContrastProcessor"/> class. /// Initializes a new instance of the <see cref="ContrastProcessor{TColor, TPacked}"/> class.
/// </summary> /// </summary>
/// <param name="contrast">The new contrast of the image. Must be between -100 and 100.</param> /// <param name="contrast">The new contrast of the image. Must be between -100 and 100.</param>
/// <exception cref="ArgumentException"> /// <exception cref="System.ArgumentException">
/// <paramref name="contrast"/> is less than -100 or is greater than 100. /// <paramref name="contrast"/> is less than -100 or is greater than 100.
/// </exception> /// </exception>
public ContrastProcessor(int contrast) public ContrastProcessor(int contrast)

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

@ -30,7 +30,7 @@ namespace ImageProcessorCore.Processors
private float[,] kernelX; private float[,] kernelX;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GuassianBlurProcessor"/> class. /// Initializes a new instance of the <see cref="BoxBlurProcessor{TColor, TPacked}"/> class.
/// </summary> /// </summary>
/// <param name="radius"> /// <param name="radius">
/// The 'radius' value representing the size of the area to sample. /// The 'radius' value representing the size of the area to sample.

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

@ -11,6 +11,8 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Defines a filter that uses a 2 dimensional matrix to perform convolution against an image. /// Defines a filter that uses a 2 dimensional matrix to perform convolution against an image.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public abstract class ConvolutionFilter<TColor, TPacked> : ImageProcessor<TColor, TPacked> public abstract class ConvolutionFilter<TColor, TPacked> : ImageProcessor<TColor, TPacked>
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct

2
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetector2DFilter.cs

@ -9,6 +9,8 @@ namespace ImageProcessorCore.Processors
/// Defines a filter that detects edges within an image using two /// Defines a filter that detects edges within an image using two
/// one-dimensional matrices. /// one-dimensional matrices.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public abstract class EdgeDetector2DFilter<TColor, TPacked> : Convolution2DFilter<TColor, TPacked>, IEdgeDetectorFilter<TColor, TPacked> public abstract class EdgeDetector2DFilter<TColor, TPacked> : Convolution2DFilter<TColor, TPacked>, IEdgeDetectorFilter<TColor, TPacked>
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct

2
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetectorFilter.cs

@ -9,6 +9,8 @@ namespace ImageProcessorCore.Processors
/// Defines a filter that detects edges within an image using a single /// Defines a filter that detects edges within an image using a single
/// two dimensional matrix. /// two dimensional matrix.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public abstract class EdgeDetectorFilter<TColor, TPacked> : ConvolutionFilter<TColor, TPacked>, IEdgeDetectorFilter<TColor, TPacked> public abstract class EdgeDetectorFilter<TColor, TPacked> : ConvolutionFilter<TColor, TPacked>, IEdgeDetectorFilter<TColor, TPacked>
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct

2
src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs

@ -8,6 +8,8 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Provides properties and methods allowing the detection of edges within an image. /// Provides properties and methods allowing the detection of edges within an image.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public interface IEdgeDetectorFilter<TColor, TPacked> : IImageProcessor<TColor, TPacked>, IEdgeDetectorFilter public interface IEdgeDetectorFilter<TColor, TPacked> : IImageProcessor<TColor, TPacked>, IEdgeDetectorFilter
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct

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

@ -37,7 +37,7 @@ namespace ImageProcessorCore.Processors
private float[,] kernelX; private float[,] kernelX;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GuassianBlurProcessor"/> class. /// Initializes a new instance of the <see cref="GuassianBlurProcessor{TColor, TPacked}"/> class.
/// </summary> /// </summary>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param> /// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
public GuassianBlurProcessor(float sigma = 3f) public GuassianBlurProcessor(float sigma = 3f)
@ -47,7 +47,7 @@ namespace ImageProcessorCore.Processors
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GuassianBlurProcessor"/> class. /// Initializes a new instance of the <see cref="GuassianBlurProcessor{TColor, TPacked}"/> class.
/// </summary> /// </summary>
/// <param name="radius"> /// <param name="radius">
/// The 'radius' value representing the size of the area to sample. /// The 'radius' value representing the size of the area to sample.
@ -59,7 +59,7 @@ namespace ImageProcessorCore.Processors
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GuassianBlurProcessor"/> class. /// Initializes a new instance of the <see cref="GuassianBlurProcessor{TColor, TPacked}"/> class.
/// </summary> /// </summary>
/// <param name="sigma"> /// <param name="sigma">
/// The 'sigma' value representing the weight of the blur. /// The 'sigma' value representing the weight of the blur.

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

@ -37,7 +37,7 @@ namespace ImageProcessorCore.Processors
private float[,] kernelX; private float[,] kernelX;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GuassianSharpenProcessor"/> class. /// Initializes a new instance of the <see cref="GuassianSharpenProcessor{TColor, TPacked}"/> class.
/// </summary> /// </summary>
/// <param name="sigma"> /// <param name="sigma">
/// The 'sigma' value representing the weight of the sharpening. /// The 'sigma' value representing the weight of the sharpening.
@ -49,7 +49,7 @@ namespace ImageProcessorCore.Processors
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GuassianSharpenProcessor"/> class. /// Initializes a new instance of the <see cref="GuassianSharpenProcessor{TColor, TPacked}"/> class.
/// </summary> /// </summary>
/// <param name="radius"> /// <param name="radius">
/// The 'radius' value representing the size of the area to sample. /// The 'radius' value representing the size of the area to sample.
@ -61,7 +61,7 @@ namespace ImageProcessorCore.Processors
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GuassianSharpenProcessor"/> class. /// Initializes a new instance of the <see cref="GuassianSharpenProcessor{TColor, TPacked}"/> class.
/// </summary> /// </summary>
/// <param name="sigma"> /// <param name="sigma">
/// The 'sigma' value representing the weight of the sharpen. /// The 'sigma' value representing the weight of the sharpen.

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

@ -15,11 +15,11 @@ namespace ImageProcessorCore.Processors
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public class GlowProcessor<TColor, TPacked> : ImageProcessor<TColor, TPacked> public class GlowProcessor<TColor, TPacked> : ImageProcessor<TColor, TPacked>
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GlowProcessor{TColor, TPacked}"/> class. /// Initializes a new instance of the <see cref="GlowProcessor{T,TP}"/> class.
/// </summary> /// </summary>
public GlowProcessor() public GlowProcessor()
{ {

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

@ -19,7 +19,7 @@ namespace ImageProcessorCore.Processors
where TPacked : struct where TPacked : struct
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PixelateProcessor{TColor, TPacked}"/> class. /// Initializes a new instance of the <see cref="PixelateProcessor{T,TP}"/> class.
/// </summary> /// </summary>
/// <param name="size">The size of the pixels. Must be greater than 0.</param> /// <param name="size">The size of the pixels. Must be greater than 0.</param>
/// <exception cref="System.ArgumentException"> /// <exception cref="System.ArgumentException">
@ -110,4 +110,4 @@ namespace ImageProcessorCore.Processors
} }
} }
} }
} }

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

@ -96,5 +96,4 @@ namespace ImageProcessorCore.Processors
} }
} }
} }
} }

2
src/ImageProcessorCore/Filters/Sepia.cs

@ -55,4 +55,4 @@ namespace ImageProcessorCore
} }
} }
} }
} }

4
src/ImageProcessorCore/Formats/Bmp/BmpDecoder.cs

@ -71,8 +71,8 @@ namespace ImageProcessorCore.Formats
/// <inheritdoc/> /// <inheritdoc/>
public void Decode<TColor, TPacked>(Image<TColor, TPacked> image, Stream stream) public void Decode<TColor, TPacked>(Image<TColor, TPacked> image, Stream stream)
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct
{ {
Guard.NotNull(image, "image"); Guard.NotNull(image, "image");
Guard.NotNull(stream, "stream"); Guard.NotNull(stream, "stream");

11
src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs

@ -128,8 +128,7 @@ namespace ImageProcessorCore.Formats
case BmpCompression.RGB: case BmpCompression.RGB:
if (this.infoHeader.HeaderSize != 40) if (this.infoHeader.HeaderSize != 40)
{ {
throw new ImageFormatException( throw new ImageFormatException($"Header Size value '{this.infoHeader.HeaderSize}' is not valid.");
$"Header Size value '{this.infoHeader.HeaderSize}' is not valid.");
} }
if (this.infoHeader.BitsPerPixel == 32) if (this.infoHeader.BitsPerPixel == 32)
@ -190,7 +189,7 @@ namespace ImageProcessorCore.Formats
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="imageData">The <see cref="T:TColor[]"/> image data to assign the palette to.</param> /// <param name="imageData">The <see cref="T:T[]"/> image data to assign the palette to.</param>
/// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param> /// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
@ -255,7 +254,7 @@ namespace ImageProcessorCore.Formats
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="imageData">The <see cref="T:TColor[]"/> image data to assign the palette to.</param> /// <param name="imageData">The <see cref="T:T[]"/> image data to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param> /// <param name="inverted">Whether the bitmap is inverted.</param>
@ -306,7 +305,7 @@ namespace ImageProcessorCore.Formats
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="imageData">The <see cref="T:TColor[]"/> image data to assign the palette to.</param> /// <param name="imageData">The <see cref="T:T[]"/> image data to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param> /// <param name="inverted">Whether the bitmap is inverted.</param>
@ -346,7 +345,7 @@ namespace ImageProcessorCore.Formats
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="imageData">The <see cref="T:TColor[]"/> image data to assign the palette to.</param> /// <param name="imageData">The <see cref="T:T[]"/> image data to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param> /// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param> /// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param> /// <param name="inverted">Whether the bitmap is inverted.</param>

13
src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs

@ -27,8 +27,8 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Encodes the image to the specified stream from the <see cref="ImageBase{TColor, TPacked}"/>. /// Encodes the image to the specified stream from the <see cref="ImageBase{TColor, TPacked}"/>.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>long, float.</example></typeparam>
/// <param name="image">The <see cref="ImageBase{TColor, TPacked}"/> to encode from.</param> /// <param name="image">The <see cref="ImageBase{TColor, TPacked}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param> /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
/// <param name="bitsPerPixel">The <see cref="BmpBitsPerPixel"/></param> /// <param name="bitsPerPixel">The <see cref="BmpBitsPerPixel"/></param>
@ -134,11 +134,11 @@ namespace ImageProcessorCore.Formats
switch (this.bmpBitsPerPixel) switch (this.bmpBitsPerPixel)
{ {
case BmpBitsPerPixel.Pixel32: case BmpBitsPerPixel.Pixel32:
this.Write32Bit(writer, pixels); this.Write32Bit<TColor, TPacked>(writer, pixels);
break; break;
case BmpBitsPerPixel.Pixel24: case BmpBitsPerPixel.Pixel24:
this.Write24Bit(writer, pixels); this.Write24Bit<TColor, TPacked>(writer, pixels);
break; break;
} }
} }
@ -150,7 +150,7 @@ namespace ImageProcessorCore.Formats
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param> /// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="pixels">The <see cref="IPixelAccessor"/> containing pixel data.</param> /// <param name="pixels">The <see cref="PixelAccessor{TColor,TPacked}"/> containing pixel data.</param>
private void Write32Bit<TColor, TPacked>(EndianBinaryWriter writer, PixelAccessor<TColor, TPacked> pixels) private void Write32Bit<TColor, TPacked>(EndianBinaryWriter writer, PixelAccessor<TColor, TPacked> pixels)
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct
@ -176,7 +176,8 @@ namespace ImageProcessorCore.Formats
/// Writes the 24bit color palette to the stream. /// Writes the 24bit color palette to the stream.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="pixels">The <see cref="PixelAccessor{TColor,TPacked}"/> containing pixel data.</param> /// <param name="pixels">The <see cref="PixelAccessor{TColor,TPacked}"/> containing pixel data.</param>
private void Write24Bit<TColor, TPacked>(EndianBinaryWriter writer, PixelAccessor<TColor, TPacked> pixels) private void Write24Bit<TColor, TPacked>(EndianBinaryWriter writer, PixelAccessor<TColor, TPacked> pixels)
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>

2
src/ImageProcessorCore/Formats/Gif/GifDecoderCore.cs

@ -12,7 +12,7 @@ namespace ImageProcessorCore.Formats
/// Performs the gif decoding operation. /// Performs the gif decoding operation.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
internal class GifDecoderCore<TColor, TPacked> internal class GifDecoderCore<TColor, TPacked>
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct

1
src/ImageProcessorCore/Formats/Gif/LzwDecoder.cs

@ -5,7 +5,6 @@
namespace ImageProcessorCore.Formats namespace ImageProcessorCore.Formats
{ {
using System;
using System.IO; using System.IO;
/// <summary> /// <summary>

2
src/ImageProcessorCore/Formats/Gif/LzwEncoder.cs

@ -310,7 +310,7 @@ namespace ImageProcessorCore.Formats
} }
/// <summary> /// <summary>
/// Return the nexTColor pixel from the image /// Return the next pixel from the image
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The <see cref="int"/> /// The <see cref="int"/>

2
src/ImageProcessorCore/Formats/Gif/Sections/GifGraphicsControlExtension.cs

@ -27,7 +27,7 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Gets or sets the transparency index. /// Gets or sets the transparency index.
/// The Transparency Index is such that when encountered, the corresponding pixel /// The Transparency Index is such that when encountered, the corresponding pixel
/// of the display device is not modified and processing goes on to the nexTColor pixel. /// of the display device is not modified and processing goes on to the next pixel.
/// </summary> /// </summary>
public int TransparencyIndex { get; set; } public int TransparencyIndex { get; set; }

4
src/ImageProcessorCore/Formats/IImageEncoder.cs

@ -38,11 +38,11 @@ namespace ImageProcessorCore.Formats
bool IsSupportedFileExtension(string extension); bool IsSupportedFileExtension(string extension);
/// <summary> /// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{T,P}"/>. /// Encodes the image to the specified stream from the <see cref="Image{TColor, TPacked}"/>.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="image">The <see cref="Image{T,P}"/> to encode from.</param> /// <param name="image">The <see cref="Image{TColor, TPacked}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param> /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
void Encode<TColor, TPacked>(Image<TColor, TPacked> image, Stream stream) void Encode<TColor, TPacked>(Image<TColor, TPacked> image, Stream stream)
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>

2
src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id

@ -1 +1 @@
508fcf1910c42f4e080fcfd9c9f22ba724c1990c c27ddeac481f3a9631dcb53248a98676c8016f42

10
src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs

@ -504,7 +504,7 @@ namespace ImageProcessorCore.Formats
// Write the image data. // Write the image data.
using (PixelAccessor<TColor, TPacked> pixels = image.Lock()) using (PixelAccessor<TColor, TPacked> pixels = image.Lock())
{ {
this.WriteSOS(pixels); this.WriteSOS<TColor, TPacked>(pixels);
} }
// Write the End Of Image marker. // Write the End Of Image marker.
@ -720,10 +720,10 @@ namespace ImageProcessorCore.Formats
switch (this.subsample) switch (this.subsample)
{ {
case JpegSubsample.Ratio444: case JpegSubsample.Ratio444:
this.Encode444(pixels); this.Encode444<TColor, TPacked>(pixels);
break; break;
case JpegSubsample.Ratio420: case JpegSubsample.Ratio420:
this.Encode420(pixels); this.Encode420<TColor, TPacked>(pixels);
break; break;
} }
@ -750,7 +750,7 @@ namespace ImageProcessorCore.Formats
{ {
for (int x = 0; x < pixels.Width; x += 8) for (int x = 0; x < pixels.Width; x += 8)
{ {
this.ToYCbCr(pixels, x, y, b, cb, cr); this.ToYCbCr<TColor, TPacked>(pixels, x, y, b, cb, cr);
prevDCY = this.WriteBlock(b, QuantIndex.Luminance, prevDCY); prevDCY = this.WriteBlock(b, QuantIndex.Luminance, prevDCY);
prevDCCb = this.WriteBlock(cb, QuantIndex.Chrominance, prevDCCb); prevDCCb = this.WriteBlock(cb, QuantIndex.Chrominance, prevDCCb);
prevDCCr = this.WriteBlock(cr, QuantIndex.Chrominance, prevDCCr); prevDCCr = this.WriteBlock(cr, QuantIndex.Chrominance, prevDCCr);
@ -784,7 +784,7 @@ namespace ImageProcessorCore.Formats
int xOff = (i & 1) * 8; int xOff = (i & 1) * 8;
int yOff = (i & 2) * 4; int yOff = (i & 2) * 4;
this.ToYCbCr(pixels, x + xOff, y + yOff, b, cb[i], cr[i]); this.ToYCbCr<TColor, TPacked>(pixels, x + xOff, y + yOff, b, cb[i], cr[i]);
prevDCY = this.WriteBlock(b, QuantIndex.Luminance, prevDCY); prevDCY = this.WriteBlock(b, QuantIndex.Luminance, prevDCY);
} }

2
src/ImageProcessorCore/Formats/Png/Filters/UpFilter.cs

@ -6,7 +6,7 @@
namespace ImageProcessorCore.Formats namespace ImageProcessorCore.Formats
{ {
/// <summary> /// <summary>
/// The Up filter is just like the Sub filter except that the pixel immediately above the currenTColor pixel, /// The Up filter is just like the Sub filter except that the pixel immediately above the current pixel,
/// rather than just to its left, is used as the predictor. /// rather than just to its left, is used as the predictor.
/// <see href="https://www.w3.org/TR/PNG-Filters.html"/> /// <see href="https://www.w3.org/TR/PNG-Filters.html"/>
/// </summary> /// </summary>

2
src/ImageProcessorCore/Formats/Png/PngDecoder.cs

@ -77,6 +77,8 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Decodes the image from the specified stream to the <see cref="ImageBase{TColor, TPacked}"/>. /// Decodes the image from the specified stream to the <see cref="ImageBase{TColor, TPacked}"/>.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="image">The <see cref="ImageBase{TColor, TPacked}"/> to decode to.</param> /// <param name="image">The <see cref="ImageBase{TColor, TPacked}"/> to decode to.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public void Decode<TColor, TPacked>(Image<TColor, TPacked> image, Stream stream) public void Decode<TColor, TPacked>(Image<TColor, TPacked> image, Stream stream)

13
src/ImageProcessorCore/Formats/Png/PngDecoderCore.cs

@ -83,13 +83,13 @@ namespace ImageProcessorCore.Formats
/// Decodes the stream to the image. /// Decodes the stream to the image.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="image">The image to decode to.</param> /// <param name="image">The image to decode to.</param>
/// <param name="stream">The stream containing image data. </param> /// <param name="stream">The stream containing image data. </param>
/// <exception cref="ImageFormatException"> /// <exception cref="ImageFormatException">
/// Thrown if the stream does not contain and end chunk. /// Thrown if the stream does not contain and end chunk.
/// </exception> /// </exception>
/// <exception cref="ArgumentOutOfRangeException"> /// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown if the image is larger than the maximum allowable size. /// Thrown if the image is larger than the maximum allowable size.
/// </exception> /// </exception>
public void Decode<TColor, TPacked>(Image<TColor, TPacked> image, Stream stream) public void Decode<TColor, TPacked>(Image<TColor, TPacked> image, Stream stream)
@ -152,11 +152,7 @@ namespace ImageProcessorCore.Formats
} }
TColor[] pixels = new TColor[this.header.Width * this.header.Height]; TColor[] pixels = new TColor[this.header.Width * this.header.Height];
this.ReadScanlines<TColor, TPacked>(dataStream, pixels); this.ReadScanlines<TColor, TPacked>(dataStream, pixels);
image.SetPixels(this.header.Width, this.header.Height, pixels); image.SetPixels(this.header.Width, this.header.Height, pixels);
} }
} }
@ -228,9 +224,10 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Reads the scanlines within the image. /// Reads the scanlines within the image.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="dataStream">The <see cref="MemoryStream"/> containing data.</param> /// <param name="dataStream">The <see cref="MemoryStream"/> containing data.</param>
/// <param name="pixels"> /// <param name="pixels"> The pixel data.</param>
/// The <see cref="T:floaTColor[]"/> containing pixel data.</param>
private void ReadScanlines<TColor, TPacked>(MemoryStream dataStream, TColor[] pixels) private void ReadScanlines<TColor, TPacked>(MemoryStream dataStream, TColor[] pixels)
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct

4
src/ImageProcessorCore/Formats/Png/PngEncoder.cs

@ -73,8 +73,8 @@ namespace ImageProcessorCore.Formats
/// <inheritdoc/> /// <inheritdoc/>
public void Encode<TColor, TPacked>(Image<TColor, TPacked> image, Stream stream) public void Encode<TColor, TPacked>(Image<TColor, TPacked> image, Stream stream)
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct
{ {
PngEncoderCore encoder = new PngEncoderCore PngEncoderCore encoder = new PngEncoderCore
{ {

2
src/ImageProcessorCore/IO/EndianBitConverter.cs

@ -246,7 +246,7 @@ namespace ImageProcessorCore.IO
/// <param name="startIndex">The start index passed in</param> /// <param name="startIndex">The start index passed in</param>
/// <param name="bytesRequired">The number of bytes required</param> /// <param name="bytesRequired">The number of bytes required</param>
/// <exception cref="System.ArgumentNullException">value is a null reference</exception> /// <exception cref="System.ArgumentNullException">value is a null reference</exception>
/// <exception cref="ArgumentOutOfRangeException"> /// <exception cref="System.ArgumentOutOfRangeException">
/// startIndex is less than zero or greater than the length of value minus bytesRequired. /// startIndex is less than zero or greater than the length of value minus bytesRequired.
/// </exception> /// </exception>
[SuppressMessage("ReSharper", "UnusedParameter.Local", Justification = "Keeps code DRY")] [SuppressMessage("ReSharper", "UnusedParameter.Local", Justification = "Keeps code DRY")]

12
src/ImageProcessorCore/Image/IImageBase.cs

@ -5,8 +5,6 @@
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using System;
/// <summary> /// <summary>
/// Encapsulates the basic properties and methods required to manipulate images in varying formats. /// Encapsulates the basic properties and methods required to manipulate images in varying formats.
/// </summary> /// </summary>
@ -27,10 +25,10 @@ namespace ImageProcessorCore
/// <param name="width">The new width of the image. Must be greater than zero.</param> /// <param name="width">The new width of the image. Must be greater than zero.</param>
/// <param name="height">The new height of the image. Must be greater than zero.</param> /// <param name="height">The new height of the image. Must be greater than zero.</param>
/// <param name="pixels">The array with pixels. Must be a multiple of the width and height.</param> /// <param name="pixels">The array with pixels. Must be a multiple of the width and height.</param>
/// <exception cref="ArgumentOutOfRangeException"> /// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0. /// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception> /// </exception>
/// <exception cref="ArgumentException"> /// <exception cref="System.ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height. /// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height.
/// </exception> /// </exception>
void SetPixels(int width, int height, TColor[] pixels); void SetPixels(int width, int height, TColor[] pixels);
@ -42,10 +40,10 @@ namespace ImageProcessorCore
/// <param name="width">The new width of the image. Must be greater than zero.</param> /// <param name="width">The new width of the image. Must be greater than zero.</param>
/// <param name="height">The new height of the image. Must be greater than zero.</param> /// <param name="height">The new height of the image. Must be greater than zero.</param>
/// <param name="pixels">The array with pixels. Must be a multiple of four times the width and height.</param> /// <param name="pixels">The array with pixels. Must be a multiple of four times the width and height.</param>
/// <exception cref="ArgumentOutOfRangeException"> /// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0. /// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception> /// </exception>
/// <exception cref="ArgumentException"> /// <exception cref="System.ArgumentException">
/// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height. /// Thrown if the <paramref name="pixels"/> length is not equal to Width * Height.
/// </exception> /// </exception>
void ClonePixels(int width, int height, TColor[] pixels); void ClonePixels(int width, int height, TColor[] pixels);
@ -56,7 +54,7 @@ namespace ImageProcessorCore
/// It is imperative that the accessor is correctly disposed off after use. /// It is imperative that the accessor is correctly disposed off after use.
/// </remarks> /// </remarks>
/// </summary> /// </summary>
/// <returns>The <see cref="IPixelAccessor"/></returns> /// <returns>The <see cref="PixelAccessor{TColor,TPacked}"/></returns>
PixelAccessor<TColor, TPacked> Lock(); PixelAccessor<TColor, TPacked> Lock();
} }

4
src/ImageProcessorCore/Image/IImageProcessor.cs

@ -44,7 +44,7 @@ namespace ImageProcessorCore.Processors
bool Compand { get; set; } bool Compand { get; set; }
/// <summary> /// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TColor, TPacked}"/>. /// Applies the process to the specified portion of the specified <see cref="ImageBase{T, TP}"/>.
/// </summary> /// </summary>
/// <param name="target">Target image to apply the process to.</param> /// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image. Cannot be null.</param> /// <param name="source">The source image. Cannot be null.</param>
@ -64,7 +64,7 @@ namespace ImageProcessorCore.Processors
void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle sourceRectangle); void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle sourceRectangle);
/// <summary> /// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase{TColor, TPacked}"/> at the specified /// Applies the process to the specified portion of the specified <see cref="ImageBase{T, TP}"/> at the specified
/// location and with the specified size. /// location and with the specified size.
/// </summary> /// </summary>
/// <param name="target">Target image to apply the process to.</param> /// <param name="target">Target image to apply the process to.</param>

53
src/ImageProcessorCore/Image/ImageBase.cs

@ -3,8 +3,6 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
using System.Runtime.CompilerServices;
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using System; using System;
@ -12,20 +10,15 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// The base class of all images. Encapsulates the basic properties and methods required to manipulate /// The base class of all images. Encapsulates the basic properties and methods required to manipulate
/// images in differenTColor pixel formats. /// images in different pixel formats.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
[DebuggerDisplay("Image: {Width}x{Height}")] [DebuggerDisplay("Image: {Width}x{Height}")]
public abstract unsafe class ImageBase<TColor, TPacked> : IImageBase<TColor, TPacked> public abstract class ImageBase<TColor, TPacked> : IImageBase<TColor, TPacked>
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct
{ {
/// <summary>
/// The image pixels
/// </summary>
private TColor[] pixelBuffer;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageBase{TColor, TPacked}"/> class. /// Initializes a new instance of the <see cref="ImageBase{TColor, TPacked}"/> class.
/// </summary> /// </summary>
@ -38,7 +31,7 @@ namespace ImageProcessorCore
/// </summary> /// </summary>
/// <param name="width">The width of the image in pixels.</param> /// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param> /// <param name="height">The height of the image in pixels.</param>
/// <exception cref="ArgumentOutOfRangeException"> /// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0. /// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception> /// </exception>
protected ImageBase(int width, int height) protected ImageBase(int width, int height)
@ -48,7 +41,7 @@ namespace ImageProcessorCore
this.Width = width; this.Width = width;
this.Height = height; this.Height = height;
this.pixelBuffer = new TColor[width * height]; this.Pixels = new TColor[width * height];
} }
/// <summary> /// <summary>
@ -68,9 +61,9 @@ namespace ImageProcessorCore
this.Height = other.Height; this.Height = other.Height;
this.CopyProperties(other); this.CopyProperties(other);
// Copy the pixels. // Copy the pixels. Don't use Unsafe.Copy as it is breaking edge detection.
this.pixelBuffer = new TColor[this.Width * this.Height]; this.Pixels = new TColor[this.Width * this.Height];
Unsafe.Copy(Unsafe.AsPointer(ref this.pixelBuffer), ref other.pixelBuffer); Array.Copy(other.Pixels, this.Pixels, other.Pixels.Length);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -80,7 +73,7 @@ namespace ImageProcessorCore
public int MaxHeight { get; set; } = int.MaxValue; public int MaxHeight { get; set; } = int.MaxValue;
/// <inheritdoc/> /// <inheritdoc/>
public TColor[] Pixels => this.pixelBuffer; public TColor[] Pixels { get; private set; }
/// <inheritdoc/> /// <inheritdoc/>
public int Width { get; private set; } public int Width { get; private set; }
@ -103,15 +96,8 @@ namespace ImageProcessorCore
/// <inheritdoc/> /// <inheritdoc/>
public void SetPixels(int width, int height, TColor[] pixels) public void SetPixels(int width, int height, TColor[] pixels)
{ {
if (width <= 0) Guard.MustBeGreaterThan(width, 0, nameof(width));
{ Guard.MustBeGreaterThan(height, 0, nameof(height));
throw new ArgumentOutOfRangeException(nameof(width), "Width must be greater than or equals than zero.");
}
if (height <= 0)
{
throw new ArgumentOutOfRangeException(nameof(height), "Height must be greater than or equal than zero.");
}
if (pixels.Length != width * height) if (pixels.Length != width * height)
{ {
@ -120,21 +106,14 @@ namespace ImageProcessorCore
this.Width = width; this.Width = width;
this.Height = height; this.Height = height;
this.pixelBuffer = pixels; this.Pixels = pixels;
} }
/// <inheritdoc/> /// <inheritdoc/>
public void ClonePixels(int width, int height, TColor[] pixels) public void ClonePixels(int width, int height, TColor[] pixels)
{ {
if (width <= 0) Guard.MustBeGreaterThan(width, 0, nameof(width));
{ Guard.MustBeGreaterThan(height, 0, nameof(height));
throw new ArgumentOutOfRangeException(nameof(width), "Width must be greater than or equals than zero.");
}
if (height <= 0)
{
throw new ArgumentOutOfRangeException(nameof(height), "Height must be greater than or equal than zero.");
}
if (pixels.Length != width * height) if (pixels.Length != width * height)
{ {
@ -144,9 +123,9 @@ namespace ImageProcessorCore
this.Width = width; this.Width = width;
this.Height = height; this.Height = height;
// Copy the pixels. // Copy the pixels. Don't use Unsafe.Copy as it is breaking edge detection.
this.pixelBuffer = new TColor[pixels.Length]; this.Pixels = new TColor[pixels.Length];
Unsafe.Copy(Unsafe.AsPointer(ref this.pixelBuffer), ref pixels); Array.Copy(pixels, this.Pixels, pixels.Length);
} }
/// <inheritdoc/> /// <inheritdoc/>

20
src/ImageProcessorCore/Image/ImageExtensions.cs

@ -20,7 +20,7 @@ namespace ImageProcessorCore
/// Saves the image to the given stream with the bmp format. /// Saves the image to the given stream with the bmp format.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param> /// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
@ -34,7 +34,7 @@ namespace ImageProcessorCore
/// Saves the image to the given stream with the png format. /// Saves the image to the given stream with the png format.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param> /// <param name="stream">The stream to save the image to.</param>
/// <param name="quality">The quality to save the image to representing the number of colors. /// <param name="quality">The quality to save the image to representing the number of colors.
@ -50,7 +50,7 @@ namespace ImageProcessorCore
/// Saves the image to the given stream with the jpeg format. /// Saves the image to the given stream with the jpeg format.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param> /// <param name="stream">The stream to save the image to.</param>
/// <param name="quality">The quality to save the image to. Between 1 and 100.</param> /// <param name="quality">The quality to save the image to. Between 1 and 100.</param>
@ -64,7 +64,7 @@ namespace ImageProcessorCore
/// Saves the image to the given stream with the gif format. /// Saves the image to the given stream with the gif format.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param> /// <param name="stream">The stream to save the image to.</param>
/// <param name="quality">The quality to save the image to representing the number of colors. Between 1 and 256.</param> /// <param name="quality">The quality to save the image to representing the number of colors. Between 1 and 256.</param>
@ -115,8 +115,8 @@ namespace ImageProcessorCore
/// This method is not chainable. /// This method is not chainable.
/// </remarks> /// </remarks>
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The source image. Cannot be null.</param> /// <param name="source">The source image. Cannot be null.</param>
/// <param name="width">The target image width.</param> /// <param name="width">The target image width.</param>
/// <param name="height">The target image height.</param> /// <param name="height">The target image height.</param>
@ -135,8 +135,8 @@ namespace ImageProcessorCore
/// This method does will resize the target image if the source and target rectangles are different. /// This method does will resize the target image if the source and target rectangles are different.
/// </remarks> /// </remarks>
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The source image. Cannot be null.</param> /// <param name="source">The source image. Cannot be null.</param>
/// <param name="width">The target image width.</param> /// <param name="width">The target image width.</param>
/// <param name="height">The target image height.</param> /// <param name="height">The target image height.</param>
@ -159,8 +159,8 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Performs the given action on the source image. /// Performs the given action on the source image.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>long, float.</example></typeparam>
/// <param name="source">The image to perform the action against.</param> /// <param name="source">The image to perform the action against.</param>
/// <param name="clone">Whether to clone the image.</param> /// <param name="clone">Whether to clone the image.</param>
/// <param name="action">The <see cref="Action"/> to perform against the image.</param> /// <param name="action">The <see cref="Action"/> to perform against the image.</param>

26
src/ImageProcessorCore/Image/PixelAccessor.cs

@ -14,7 +14,7 @@ namespace ImageProcessorCore
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public sealed unsafe class PixelAccessor<TColor, TPacked> : IDisposable public unsafe class PixelAccessor<TColor, TPacked> : IDisposable
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct
{ {
@ -104,8 +104,28 @@ namespace ImageProcessorCore
/// <returns>The <see cref="TColor"/> at the specified position.</returns> /// <returns>The <see cref="TColor"/> at the specified position.</returns>
public TColor this[int x, int y] public TColor this[int x, int y]
{ {
get { return Unsafe.Read<TColor>(pixelsBase + ((y * this.RowStride) + (x * this.PixelSize))); } get { return Unsafe.Read<TColor>(this.pixelsBase + (y * this.Width + x) * Unsafe.SizeOf<TColor>()); }
set { Unsafe.Write(pixelsBase + ((y * this.RowStride) + (x * this.PixelSize)), value); } set { Unsafe.Write(this.pixelsBase + (y * this.Width + x) * Unsafe.SizeOf<TColor>(), value); }
}
/// <summary>
/// Copies an entire row of pixels.
/// </summary>
/// <param name="sourceX">The x-coordinate of the source row.</param>
/// <param name="sourceY">The y-coordinate of the source row.</param>
/// <param name="target">The target pixel buffer accessor.</param>
/// <param name="targetX">The x-coordinate of the target row.</param>
/// <param name="targetY">The y-coordinate of the target row.</param>
/// <param name="pixelCount">The number of pixels to copy</param>
public void CopyRow(int sourceX, int sourceY, PixelAccessor<TColor, TPacked> target, int targetX, int targetY, int pixelCount)
{
int size = Unsafe.SizeOf<TColor>();
byte* sourcePtr = this.pixelsBase + (sourceY * this.Width + sourceX) * size;
byte* targetPtr = target.pixelsBase + (targetY * target.Width + targetX) * size;
uint byteCount = (uint)(pixelCount * size);
Unsafe.CopyBlock(targetPtr, sourcePtr, byteCount);
} }
/// <summary> /// <summary>

4
src/ImageProcessorCore/ImageProcessor.cs

@ -12,6 +12,8 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Allows the application of processors to images. /// Allows the application of processors to images.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public abstract class ImageProcessor<TColor, TPacked> : IImageProcessor<TColor, TPacked> public abstract class ImageProcessor<TColor, TPacked> : IImageProcessor<TColor, TPacked>
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct
@ -93,8 +95,6 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// This method is called before the process is applied to prepare the processor. /// This method is called before the process is applied to prepare the processor.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="target">Target image to apply the process to.</param> /// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image. Cannot be null.</param> /// <param name="source">The source image. Cannot be null.</param>
/// <param name="targetRectangle"> /// <param name="targetRectangle">

219
src/ImageProcessorCore/Numerics/BigRational.cs

@ -0,0 +1,219 @@
// <copyright file="Rational.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore
{
using System;
using System.Text;
internal struct BigRational : IEquatable<BigRational>
{
private bool IsIndeterminate
{
get
{
if (Denominator != 0)
return false;
return Numerator == 0;
}
}
private bool IsInteger => Denominator == 1;
private bool IsNegativeInfinity
{
get
{
if (Denominator != 0)
return false;
return Numerator == -1;
}
}
private bool IsPositiveInfinity
{
get
{
if (Denominator != 0)
return false;
return Numerator == 1;
}
}
private bool IsZero
{
get
{
if (Denominator != 1)
return false;
return Numerator == 0;
}
}
private static long GreatestCommonDivisor(long a, long b)
{
return b == 0 ? a : GreatestCommonDivisor(b, a % b);
}
private void Simplify()
{
if (IsIndeterminate)
return;
if (IsNegativeInfinity)
return;
if (IsPositiveInfinity)
return;
if (IsInteger)
return;
if (IsZero)
return;
if (Numerator == 0)
{
Denominator = 0;
return;
}
if (Numerator == Denominator)
{
Numerator = 1;
Denominator = 1;
}
long gcd = GreatestCommonDivisor(Math.Abs(Numerator), Math.Abs(Denominator));
if (gcd > 1)
{
Numerator = Numerator / gcd;
Denominator = Denominator / gcd;
}
}
public BigRational(long numerator, long denominator)
: this(numerator, denominator, false)
{
}
public BigRational(long numerator, long denominator, bool simplify)
{
Numerator = numerator;
Denominator = denominator;
if (simplify)
Simplify();
}
public BigRational(double value, bool bestPrecision)
{
if (double.IsNaN(value))
{
Numerator = Denominator = 0;
return;
}
if (double.IsPositiveInfinity(value))
{
Numerator = 1;
Denominator = 0;
return;
}
if (double.IsNegativeInfinity(value))
{
Numerator = -1;
Denominator = 0;
return;
}
Numerator = 1;
Denominator = 1;
double val = Math.Abs(value);
double df = Numerator / Denominator;
double epsilon = bestPrecision ? double.Epsilon : .000001;
while (Math.Abs(df - val) > epsilon)
{
if (df < val)
Numerator++;
else
{
Denominator++;
Numerator = (int)(val * Denominator);
}
df = Numerator / (double)Denominator;
}
if (value < 0.0)
Numerator *= -1;
Simplify();
}
public long Denominator
{
get;
private set;
}
public long Numerator
{
get;
private set;
}
public bool Equals(BigRational other)
{
if (Denominator == other.Denominator)
return Numerator == other.Numerator;
if (Numerator == 0 && Denominator == 0)
return other.Numerator == 0 && other.Denominator == 0;
if (other.Numerator == 0 && other.Denominator == 0)
return Numerator == 0 && Denominator == 0;
return (Numerator * other.Denominator) == (Denominator * other.Numerator);
}
public override int GetHashCode()
{
return ((Numerator * 397) ^ Denominator).GetHashCode();
}
public string ToString(IFormatProvider provider)
{
if (IsIndeterminate)
return "[ Indeterminate ]";
if (IsPositiveInfinity)
return "[ PositiveInfinity ]";
if (IsNegativeInfinity)
return "[ NegativeInfinity ]";
if (IsZero)
return "0";
if (IsInteger)
return Numerator.ToString(provider);
StringBuilder sb = new StringBuilder();
sb.Append(Numerator.ToString(provider));
sb.Append("/");
sb.Append(Denominator.ToString(provider));
return sb.ToString();
}
}
}

545
src/ImageProcessorCore/Numerics/Rational.cs

@ -7,11 +7,9 @@ namespace ImageProcessorCore
{ {
using System; using System;
using System.Globalization; using System.Globalization;
using System.Numerics;
using System.Text;
/// <summary> /// <summary>
/// Represents a number that can be expressed as a fraction /// Represents a number that can be expressed as a fraction.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This is a very simplified implimentation of a rational number designed for use with metadata only. /// This is a very simplified implimentation of a rational number designed for use with metadata only.
@ -19,532 +17,183 @@ namespace ImageProcessorCore
public struct Rational : IEquatable<Rational> public struct Rational : IEquatable<Rational>
{ {
/// <summary> /// <summary>
/// Represents a rational object that is not a number; NaN (0, 0) /// Initializes a new instance of the <see cref="Rational"/> struct.
/// </summary> /// </summary>
public static Rational Indeterminate = new Rational(BigInteger.Zero); ///<param name="value">The <see cref="double"/> to convert to an instance of this type.</param>
public Rational(double value)
/// <summary> : this(value, false)
/// Represents a rational object that is equal to 0 (0, 1)
/// </summary>
public static Rational Zero = new Rational(BigInteger.Zero);
/// <summary>
/// Represents a rational object that is equal to 1 (1, 1)
/// </summary>
public static Rational One = new Rational(BigInteger.One);
/// <summary>
/// Represents a Rational object that is equal to negative infinity (-1, 0)
/// </summary>
public static readonly Rational NegativeInfinity = new Rational(BigInteger.MinusOne, BigInteger.Zero);
/// <summary>
/// Represents a Rational object that is equal to positive infinity (1, 0)
/// </summary>
public static readonly Rational PositiveInfinity = new Rational(BigInteger.One, BigInteger.Zero);
/// <summary>
/// The maximum number of decimal places
/// </summary>
private const int DoubleMaxScale = 308;
/// <summary>
/// The maximum precision (numbers after the decimal point)
/// </summary>
private static readonly BigInteger DoublePrecision = BigInteger.Pow(10, DoubleMaxScale);
/// <summary>
/// Represents double.MaxValue
/// </summary>
private static readonly BigInteger DoubleMaxValue = (BigInteger)double.MaxValue;
/// <summary>
/// Represents double.MinValue
/// </summary>
private static readonly BigInteger DoubleMinValue = (BigInteger)double.MinValue;
/// <summary>
/// Initializes a new instance of the <see cref="Rational"/> struct.
/// </summary>
/// <param name="numerator">
/// The number above the line in a vulgar fraction showing how many of the parts
/// indicated by the denominator are taken.
/// </param>
/// <param name="denominator">
/// The number below the line in a vulgar fraction; a divisor.
/// </param>
public Rational(uint numerator, uint denominator)
: this()
{
this.Numerator = numerator;
this.Denominator = denominator;
this.Simplify();
}
/// <summary>
/// Initializes a new instance of the <see cref="Rational"/> struct.
/// </summary>
/// <param name="numerator">
/// The number above the line in a vulgar fraction showing how many of the parts
/// indicated by the denominator are taken.
/// </param>
/// <param name="denominator">
/// The number below the line in a vulgar fraction; a divisor.
/// </param>
public Rational(int numerator, int denominator)
: this()
{ {
this.Numerator = numerator;
this.Denominator = denominator;
this.Simplify();
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Rational"/> struct. /// Initializes a new instance of the <see cref="Rational"/> struct.
/// </summary> /// </summary>
/// <param name="numerator"> ///<param name="value">The <see cref="double"/> to convert to an instance of this type.</param>
/// The number above the line in a vulgar fraction showing how many of the parts ///<param name="bestPrecision">Specifies if the instance should be created with the best precision possible.</param>
/// indicated by the denominator are taken. public Rational(double value, bool bestPrecision)
/// </param>
/// <param name="denominator">
/// The number below the line in a vulgar fraction; a divisor.
/// </param>
public Rational(BigInteger numerator, BigInteger denominator)
: this()
{ {
this.Numerator = numerator; BigRational rational = new BigRational(Math.Abs(value), bestPrecision);
this.Denominator = denominator;
this.Simplify(); Numerator = (uint)rational.Numerator;
Denominator = (uint)rational.Denominator;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Rational"/> struct. /// Initializes a new instance of the <see cref="Rational"/> struct.
/// </summary> /// </summary>
/// <param name="value">The big integer to create the rational from.</param> /// <param name="value">The integer to create the rational from.</param>
public Rational(BigInteger value) public Rational(uint value)
: this() : this(value, 1)
{ {
this.Numerator = value;
this.Denominator = BigInteger.One;
this.Simplify();
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Rational"/> struct. /// Initializes a new instance of the <see cref="Rational"/> struct.
/// </summary> /// </summary>
/// <param name="value">The double to create the rational from.</param> /// <param name="numerator">The number above the line in a vulgar fraction showing how many of the parts indicated by the denominator are taken.</param>
public Rational(double value) /// <param name="denominator">The number below the line in a vulgar fraction; a divisor.</param>
: this() public Rational(uint numerator, uint denominator)
{ : this(numerator, denominator, true)
if (double.IsNaN(value))
{
this = Indeterminate;
return;
}
if (double.IsPositiveInfinity(value))
{
this = PositiveInfinity;
return;
}
if (double.IsNegativeInfinity(value))
{
this = NegativeInfinity;
return;
}
// TODO: Not happy with parsing a string like this. I should be able to use maths but maths is HARD!
this = Parse(value.ToString("R", CultureInfo.InvariantCulture));
}
/// <summary>
/// Gets the numerator of a number.
/// </summary>
public BigInteger Numerator { get; private set; }
/// <summary>
/// Gets the denominator of a number.
/// </summary>
public BigInteger Denominator { get; private set; }
/// <summary>
/// Gets a value indicating whether this instance is indeterminate.
/// </summary>
public bool IsIndeterminate
{ {
get
{
if (this.Denominator != BigInteger.Zero)
{
return false;
}
return this.Numerator == BigInteger.Zero;
}
} }
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is an integer (n, 1) /// Initializes a new instance of the <see cref="Rational"/> struct.
/// </summary> /// </summary>
public bool IsInteger => this.Denominator == BigInteger.One; /// <param name="numerator">The number above the line in a vulgar fraction showing how many of the parts indicated by the denominator are taken.</param>
/// <param name="denominator">The number below the line in a vulgar fraction; a divisor.</param>
/// <summary> /// <param name="simplify">Specified if the rational should be simplified.</param>
/// Gets a value indicating whether this instance is equal to 0 (0, 1) public Rational(uint numerator, uint denominator, bool simplify)
/// </summary>
public bool IsZero
{ {
get BigRational rational = new BigRational(numerator, denominator, simplify);
{
if (this.Denominator != BigInteger.One)
{
return false;
}
return this.Numerator == BigInteger.Zero; Numerator = (uint)rational.Numerator;
} Denominator = (uint)rational.Denominator;
} }
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is equal to 1 (1, 1) /// Determines whether the specified <see cref="Rational"/> instances are considered equal.
/// </summary> /// </summary>
public bool IsOne /// <param name="left">The first <see cref="Rational"/> to compare.</param>
/// <param name="right"> The second <see cref="Rational"/> to compare.</param>
/// <returns></returns>
public static bool operator ==(Rational left, Rational right)
{ {
get return Equals(left, right);
{
if (this.Denominator != BigInteger.One)
{
return false;
}
return this.Numerator == BigInteger.One;
}
} }
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is equal to negative infinity (-1, 0) /// Determines whether the specified <see cref="Rational"/> instances are not considered equal.
/// </summary> /// </summary>
public bool IsNegativeInfinity /// <param name="left">The first <see cref="Rational"/> to compare.</param>
/// <param name="right"> The second <see cref="Rational"/> to compare.</param>
/// <returns></returns>
public static bool operator !=(Rational left, Rational right)
{ {
get return !Equals(left, right);
{
if (this.Denominator != BigInteger.Zero)
{
return false;
}
return this.Numerator == BigInteger.MinusOne;
}
} }
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is equal to positive infinity (1, 0) /// Gets the numerator of a number.
/// </summary> /// </summary>
public bool IsPositiveInfinity public uint Numerator
{ {
get get;
{ private set;
if (this.Denominator != BigInteger.Zero)
{
return false;
}
return this.Numerator == BigInteger.One;
}
} }
/// <summary> /// <summary>
/// Converts a rational number to the nearest double. /// Gets the denominator of a number.
/// </summary> /// </summary>
/// <returns> public uint Denominator
/// The <see cref="double"/>.
/// </returns>
public double ToDouble()
{ {
// Shortcut return values get;
if (this.IsIndeterminate) private set;
{
return double.NaN;
}
if (this.IsPositiveInfinity)
{
return double.PositiveInfinity;
}
if (this.IsNegativeInfinity)
{
return double.NegativeInfinity;
}
if (this.IsInteger)
{
return (double)this.Numerator;
}
// The Double value type represents a double-precision 64-bit number with
// values ranging from -1.79769313486232e308 to +1.79769313486232e308
// values that do not fit into this range are returned as +/-Infinity
if (SafeCastToDouble(this.Numerator) && SafeCastToDouble(this.Denominator))
{
return (double)this.Numerator / (double)this.Denominator;
}
// Scale the numerator to preserve the fraction part through the integer division
// We could probably adjust this to make it less precise if need be.
BigInteger denormalized = (this.Numerator * DoublePrecision) / this.Denominator;
if (denormalized.IsZero)
{
// underflow to -+0
return (this.Numerator.Sign < 0) ? BitConverter.Int64BitsToDouble(unchecked((long)0x8000000000000000)) : 0d;
}
double result = 0;
bool isDouble = false;
int scale = DoubleMaxScale;
while (scale > 0)
{
if (!isDouble)
{
if (SafeCastToDouble(denormalized))
{
result = (double)denormalized;
isDouble = true;
}
else
{
denormalized = denormalized / 10;
}
}
result = result / 10;
scale--;
}
if (!isDouble)
{
return (this.Numerator.Sign < 0) ? double.NegativeInfinity : double.PositiveInfinity;
}
else
{
return result;
}
} }
/// <inheritdoc/> ///<summary>
/// Determines whether the specified <see cref="object"/> is equal to this <see cref="Rational"/>.
///</summary>
///<param name="obj">The <see cref="object"/> to compare this <see cref="Rational"/> with.</param>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is Rational) if (obj is Rational)
{ return Equals((Rational)obj);
return this.Equals((Rational)obj);
}
return false; return false;
} }
/// <inheritdoc/> ///<summary>
/// Determines whether the specified <see cref="Rational"/> is equal to this <see cref="Rational"/>.
///</summary>
///<param name="other">The <see cref="Rational"/> to compare this <see cref="Rational"/> with.</param>
public bool Equals(Rational other) public bool Equals(Rational other)
{ {
// Standard: a/b = c/d BigRational left = new BigRational(Numerator, Denominator);
if (this.Denominator == other.Denominator) BigRational right = new BigRational(other.Numerator, other.Denominator);
{
return this.Numerator == other.Numerator;
}
// Indeterminate
if (this.Numerator == BigInteger.Zero && this.Denominator == BigInteger.Zero)
{
return other.Numerator == BigInteger.Zero && other.Denominator == BigInteger.Zero;
}
if (other.Numerator == BigInteger.Zero && other.Denominator == BigInteger.Zero)
{
return this.Numerator == BigInteger.Zero && this.Denominator == BigInteger.Zero;
}
// ad = bc return left.Equals(right);
return (this.Numerator * other.Denominator) == (this.Denominator * other.Numerator);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return this.GetHashCode(this);
} }
/// <inheritdoc/> ///<summary>
public override string ToString() /// Converts the specified <see cref="double"/> to an instance of this type.
///</summary>
///<param name="value">The <see cref="double"/> to convert to an instance of this type.</param>
public static Rational FromDouble(double value)
{ {
return this.ToString(CultureInfo.InvariantCulture); return new Rational(value, false);
} }
/// <summary> ///<summary>
/// Converts the numeric value of this instance to its equivalent string representation using /// Converts the specified <see cref="double"/> to an instance of this type.
/// the specified culture-specific format information. ///</summary>
/// </summary> ///<param name="value">The <see cref="double"/> to convert to an instance of this type.</param>
/// <param name="provider"> ///<param name="bestPrecision">Specifies if the instance should be created with the best precision possible.</param>
/// An object that supplies culture-specific formatting information. public static Rational FromDouble(double value, bool bestPrecision)
/// </param>
/// <returns></returns>
public string ToString(IFormatProvider provider)
{ {
if (this.IsIndeterminate) return new Rational(value, bestPrecision);
{
return "[ Indeterminate ]";
}
if (this.IsPositiveInfinity)
{
return "[ PositiveInfinity ]";
}
if (this.IsNegativeInfinity)
{
return "[ NegativeInfinity ]";
}
if (this.IsZero)
{
return "0";
}
if (this.IsInteger)
{
return this.Numerator.ToString(provider);
}
StringBuilder sb = new StringBuilder();
sb.Append(this.Numerator.ToString("R", provider));
sb.Append("/");
sb.Append(this.Denominator.ToString("R", provider));
return sb.ToString();
} }
/// <summary> ///<summary>
/// Simplifies the rational. /// Serves as a hash of this type.
/// </summary> ///</summary>
private void Simplify() public override int GetHashCode()
{ {
if (this.IsIndeterminate) BigRational self = new BigRational(Numerator, Denominator);
{ return self.GetHashCode();
return;
}
if (this.IsNegativeInfinity)
{
return;
}
if (this.IsPositiveInfinity)
{
return;
}
if (this.IsInteger)
{
return;
}
if (this.Numerator == BigInteger.Zero)
{
this.Denominator = BigInteger.One;
return;
}
if (this.Numerator == this.Denominator)
{
this.Numerator = BigInteger.One;
this.Denominator = BigInteger.One;
return;
}
BigInteger gcd = BigInteger.GreatestCommonDivisor(this.Numerator, this.Denominator);
if (gcd > BigInteger.One)
{
this.Numerator = this.Numerator / gcd;
this.Denominator = this.Denominator / gcd;
}
} }
/// <summary> /// <summary>
/// Converts the string representation of a number into its rational value /// Converts a rational number to the nearest <see cref="double"/>.
/// </summary> /// </summary>
/// <param name="value">A string that contains a number to convert.</param> /// <returns>
/// <returns>The <see cref="Rational"/></returns> /// The <see cref="double"/>.
internal static Rational Parse(string value) /// </returns>
public double ToDouble()
{ {
int periodIndex = value.IndexOf(".", StringComparison.Ordinal); return Numerator / (double)Denominator;
int eIndex = value.IndexOf("E", StringComparison.Ordinal);
int slashIndex = value.IndexOf("/", StringComparison.Ordinal);
// An integer such as 3
if (periodIndex == -1 && eIndex == -1 && slashIndex == -1)
{
return new Rational(BigInteger.Parse(value));
}
// A fraction such as 22/7
if (periodIndex == -1 && eIndex == -1 && slashIndex != -1)
{
return new Rational(BigInteger.Parse(value.Substring(0, slashIndex)),
BigInteger.Parse(value.Substring(slashIndex + 1)));
}
// No scientific Notation such as 3.14159
if (eIndex == -1)
{
BigInteger n = BigInteger.Parse(value.Replace(".", string.Empty));
BigInteger d = (BigInteger)Math.Pow(10, value.Length - periodIndex - 1);
return new Rational(n, d);
}
// Scientific notation such as 3.14159E-2
int characteristic = int.Parse(value.Substring(eIndex + 1));
BigInteger ten = 10;
BigInteger numerator = BigInteger.Parse(value.Substring(0, eIndex).Replace(".", string.Empty));
BigInteger denominator = new BigInteger(Math.Pow(10, eIndex - periodIndex - 1));
BigInteger charPower = BigInteger.Pow(ten, Math.Abs(characteristic));
if (characteristic > 0)
{
numerator = numerator * charPower;
}
else
{
denominator = denominator * charPower;
}
return new Rational(numerator, denominator);
} }
/// <summary> /// <summary>
/// Returns a value indicating whether the given big integer can be /// Converts the numeric value of this instance to its equivalent string representation.
/// safely cast to a double.
/// </summary> /// </summary>
/// <param name="value">The value to test.</param> public override string ToString()
/// <returns><c>true</c> if the value can be safely cast</returns>
private static bool SafeCastToDouble(BigInteger value)
{ {
return DoubleMinValue <= value && value <= DoubleMaxValue; return ToString(CultureInfo.InvariantCulture);
} }
/// <summary> /// <summary>
/// Returns the hash code for this instance. /// Converts the numeric value of this instance to its equivalent string representation using
/// the specified culture-specific format information.
/// </summary> /// </summary>
/// <param name="rational"> /// <param name="provider">
/// The instance of <see cref="Rational"/> to return the hash code for. /// An object that supplies culture-specific formatting information.
/// </param> /// </param>
/// <returns> /// <returns></returns>
/// A 32-bit signed integer that is the hash code for this instance. public string ToString(IFormatProvider provider)
/// </returns>
private int GetHashCode(Rational rational)
{ {
return ((rational.Numerator * 397) ^ rational.Denominator).GetHashCode(); BigRational rational = new BigRational(Numerator, Denominator);
return rational.ToString(provider);
} }
} }
} }

199
src/ImageProcessorCore/Numerics/SignedRational.cs

@ -0,0 +1,199 @@
// <copyright file="Rational.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore
{
using System;
using System.Globalization;
/// <summary>
/// Represents a number that can be expressed as a fraction.
/// </summary>
/// <remarks>
/// This is a very simplified implimentation of a rational number designed for use with metadata only.
/// </remarks>
public struct SignedRational : IEquatable<SignedRational>
{
/// <summary>
/// Initializes a new instance of the <see cref="SignedRational"/> struct.
/// </summary>
///<param name="value">The <see cref="double"/> to convert to an instance of this type.</param>
public SignedRational(double value)
: this(value, false)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SignedRational"/> struct.
/// </summary>
///<param name="value">The <see cref="double"/> to convert to an instance of this type.</param>
///<param name="bestPrecision">Specifies if the instance should be created with the best precision possible.</param>
public SignedRational(double value, bool bestPrecision)
{
BigRational rational = new BigRational(value, bestPrecision);
Numerator = (int)rational.Numerator;
Denominator = (int)rational.Denominator;
}
/// <summary>
/// Initializes a new instance of the <see cref="SignedRational"/> struct.
/// </summary>
/// <param name="value">The integer to create the rational from.</param>
public SignedRational(int value)
: this(value, 1)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SignedRational"/> struct.
/// </summary>
/// <param name="numerator">The number above the line in a vulgar fraction showing how many of the parts indicated by the denominator are taken.</param>
/// <param name="denominator">The number below the line in a vulgar fraction; a divisor.</param>
public SignedRational(int numerator, int denominator)
: this(numerator, denominator, true)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SignedRational"/> struct.
/// </summary>
/// <param name="numerator">The number above the line in a vulgar fraction showing how many of the parts indicated by the denominator are taken.</param>
/// <param name="denominator">The number below the line in a vulgar fraction; a divisor.</param>
/// <param name="simplify">Specified if the rational should be simplified.</param>
public SignedRational(int numerator, int denominator, bool simplify)
{
BigRational rational = new BigRational(numerator, denominator, simplify);
Numerator = (int)rational.Numerator;
Denominator = (int)rational.Denominator;
}
/// <summary>
/// Determines whether the specified <see cref="SignedRational"/> instances are considered equal.
/// </summary>
/// <param name="left">The first <see cref="SignedRational"/> to compare.</param>
/// <param name="right"> The second <see cref="SignedRational"/> to compare.</param>
/// <returns></returns>
public static bool operator ==(SignedRational left, SignedRational right)
{
return Equals(left, right);
}
/// <summary>
/// Determines whether the specified <see cref="SignedRational"/> instances are not considered equal.
/// </summary>
/// <param name="left">The first <see cref="SignedRational"/> to compare.</param>
/// <param name="right"> The second <see cref="SignedRational"/> to compare.</param>
/// <returns></returns>
public static bool operator !=(SignedRational left, SignedRational right)
{
return !Equals(left, right);
}
/// <summary>
/// Gets the numerator of a number.
/// </summary>
public int Numerator
{
get;
private set;
}
/// <summary>
/// Gets the denominator of a number.
/// </summary>
public int Denominator
{
get;
private set;
}
///<summary>
/// Determines whether the specified <see cref="object"/> is equal to this <see cref="SignedRational"/>.
///</summary>
///<param name="obj">The <see cref="object"/> to compare this <see cref="SignedRational"/> with.</param>
public override bool Equals(object obj)
{
if (obj is SignedRational)
return Equals((SignedRational)obj);
return false;
}
///<summary>
/// Determines whether the specified <see cref="SignedRational"/> is equal to this <see cref="SignedRational"/>.
///</summary>
///<param name="other">The <see cref="SignedRational"/> to compare this <see cref="SignedRational"/> with.</param>
public bool Equals(SignedRational other)
{
BigRational left = new BigRational(Numerator, Denominator);
BigRational right = new BigRational(other.Numerator, other.Denominator);
return left.Equals(right);
}
///<summary>
/// Converts the specified <see cref="double"/> to an instance of this type.
///</summary>
///<param name="value">The <see cref="double"/> to convert to an instance of this type.</param>
public static SignedRational FromDouble(double value)
{
return new SignedRational(value, false);
}
///<summary>
/// Converts the specified <see cref="double"/> to an instance of this type.
///</summary>
///<param name="value">The <see cref="double"/> to convert to an instance of this type.</param>
///<param name="bestPrecision">Specifies if the instance should be created with the best precision possible.</param>
public static SignedRational FromDouble(double value, bool bestPrecision)
{
return new SignedRational(value, bestPrecision);
}
///<summary>
/// Serves as a hash of this type.
///</summary>
public override int GetHashCode()
{
BigRational self = new BigRational(Numerator, Denominator);
return self.GetHashCode();
}
/// <summary>
/// Converts a rational number to the nearest <see cref="double"/>.
/// </summary>
/// <returns>
/// The <see cref="double"/>.
/// </returns>
public double ToDouble()
{
return Numerator / (double)Denominator;
}
/// <summary>
/// Converts the numeric value of this instance to its equivalent string representation.
/// </summary>
public override string ToString()
{
return ToString(CultureInfo.InvariantCulture);
}
/// <summary>
/// Converts the numeric value of this instance to its equivalent string representation using
/// the specified culture-specific format information.
/// </summary>
/// <param name="provider">
/// An object that supplies culture-specific formatting information.
/// </param>
/// <returns></returns>
public string ToString(IFormatProvider provider)
{
BigRational rational = new BigRational(Numerator, Denominator);
return rational.ToString(provider);
}
}
}

2
src/ImageProcessorCore/Profiles/Exif/ExifProfile.cs

@ -2,9 +2,9 @@
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO; using System.IO;

10
src/ImageProcessorCore/Profiles/Exif/ExifReader.cs

@ -442,13 +442,13 @@ namespace ImageProcessorCore
{ {
if (!this.ValidateArray(data, 8, 4)) if (!this.ValidateArray(data, 8, 4))
{ {
return Rational.Zero; return new Rational();
} }
uint numerator = BitConverter.ToUInt32(data, 0); uint numerator = BitConverter.ToUInt32(data, 0);
uint denominator = BitConverter.ToUInt32(data, 4); uint denominator = BitConverter.ToUInt32(data, 4);
return new Rational(numerator, denominator); return new Rational(numerator, denominator, false);
} }
private sbyte ToSignedByte(byte[] data) private sbyte ToSignedByte(byte[] data)
@ -466,17 +466,17 @@ namespace ImageProcessorCore
return BitConverter.ToInt32(data, 0); return BitConverter.ToInt32(data, 0);
} }
private Rational ToSignedRational(byte[] data) private SignedRational ToSignedRational(byte[] data)
{ {
if (!this.ValidateArray(data, 8, 4)) if (!this.ValidateArray(data, 8, 4))
{ {
return Rational.Zero; return new SignedRational();
} }
int numerator = BitConverter.ToInt32(data, 0); int numerator = BitConverter.ToInt32(data, 0);
int denominator = BitConverter.ToInt32(data, 4); int denominator = BitConverter.ToInt32(data, 4);
return new Rational(numerator, denominator); return new SignedRational(numerator, denominator, false);
} }
private short ToSignedShort(byte[] data) private short ToSignedShort(byte[] data)

10
src/ImageProcessorCore/Profiles/Exif/ExifValue.cs

@ -539,13 +539,12 @@ namespace ImageProcessorCore
case ExifDataType.DoubleFloat: case ExifDataType.DoubleFloat:
Guard.IsTrue(type == typeof(double), nameof(value), $"Value should be a double{(this.IsArray ? " array." : ".")}"); Guard.IsTrue(type == typeof(double), nameof(value), $"Value should be a double{(this.IsArray ? " array." : ".")}");
break; break;
case ExifDataType.Rational:
case ExifDataType.SignedRational:
Guard.IsTrue(type == typeof(Rational), nameof(value), $"Value should be a Rational{(this.IsArray ? " array." : ".")}");
break;
case ExifDataType.Long: case ExifDataType.Long:
Guard.IsTrue(type == typeof(uint), nameof(value), $"Value should be an unsigned int{(this.IsArray ? " array." : ".")}"); Guard.IsTrue(type == typeof(uint), nameof(value), $"Value should be an unsigned int{(this.IsArray ? " array." : ".")}");
break; break;
case ExifDataType.Rational:
Guard.IsTrue(type == typeof(Rational), nameof(value), $"Value should be a Rational{(this.IsArray ? " array." : ".")}");
break;
case ExifDataType.Short: case ExifDataType.Short:
Guard.IsTrue(type == typeof(ushort), nameof(value), $"Value should be an unsigned short{(this.IsArray ? " array." : ".")}"); Guard.IsTrue(type == typeof(ushort), nameof(value), $"Value should be an unsigned short{(this.IsArray ? " array." : ".")}");
break; break;
@ -555,6 +554,9 @@ namespace ImageProcessorCore
case ExifDataType.SignedLong: case ExifDataType.SignedLong:
Guard.IsTrue(type == typeof(int), nameof(value), $"Value should be an int{(this.IsArray ? " array." : ".")}"); Guard.IsTrue(type == typeof(int), nameof(value), $"Value should be an int{(this.IsArray ? " array." : ".")}");
break; break;
case ExifDataType.SignedRational:
Guard.IsTrue(type == typeof(SignedRational), nameof(value), $"Value should be a SignedRational{(this.IsArray ? " array." : ".")}");
break;
case ExifDataType.SignedShort: case ExifDataType.SignedShort:
Guard.IsTrue(type == typeof(short), nameof(value), $"Value should be a short{(this.IsArray ? " array." : ".")}"); Guard.IsTrue(type == typeof(short), nameof(value), $"Value should be a short{(this.IsArray ? " array." : ".")}");
break; break;

13
src/ImageProcessorCore/Profiles/Exif/ExifWriter.cs

@ -329,17 +329,16 @@ namespace ImageProcessorCore
private int WriteRational(Rational value, byte[] destination, int offset) private int WriteRational(Rational value, byte[] destination, int offset)
{ {
// Ensure no overflow Write(BitConverter.GetBytes(value.Numerator), destination, offset);
Write(BitConverter.GetBytes((uint)(value.Numerator * (value.ToDouble() < 0.0 ? -1 : 1))), destination, offset); Write(BitConverter.GetBytes(value.Denominator), destination, offset + 4);
Write(BitConverter.GetBytes((uint)value.Denominator), destination, offset + 4);
return offset + 8; return offset + 8;
} }
private int WriteSignedRational(Rational value, byte[] destination, int offset) private int WriteSignedRational(SignedRational value, byte[] destination, int offset)
{ {
Write(BitConverter.GetBytes((int)value.Numerator), destination, offset); Write(BitConverter.GetBytes(value.Numerator), destination, offset);
Write(BitConverter.GetBytes((int)value.Denominator), destination, offset + 4); Write(BitConverter.GetBytes(value.Denominator), destination, offset + 4);
return offset + 8; return offset + 8;
} }
@ -370,7 +369,7 @@ namespace ImageProcessorCore
case ExifDataType.SignedShort: case ExifDataType.SignedShort:
return Write(BitConverter.GetBytes((short)value), destination, offset); return Write(BitConverter.GetBytes((short)value), destination, offset);
case ExifDataType.SignedRational: case ExifDataType.SignedRational:
return this.WriteSignedRational((Rational)value, destination, offset); return this.WriteSignedRational((SignedRational)value, destination, offset);
case ExifDataType.SingleFloat: case ExifDataType.SingleFloat:
return Write(BitConverter.GetBytes((float)value), destination, offset); return Write(BitConverter.GetBytes((float)value), destination, offset);
default: default:

2
src/ImageProcessorCore/Quantizers/IQuantizer.cs

@ -15,7 +15,7 @@ namespace ImageProcessorCore.Quantizers
where TPacked : struct where TPacked : struct
{ {
/// <summary> /// <summary>
/// Quantize an image and return the resulting outpuTColor pixels. /// Quantize an image and return the resulting output pixels.
/// </summary> /// </summary>
/// <param name="image">The image to quantize.</param> /// <param name="image">The image to quantize.</param>
/// <param name="maxColors">The maximum number of colors to return.</param> /// <param name="maxColors">The maximum number of colors to return.</param>

24
src/ImageProcessorCore/Quantizers/Octree/OctreeQuantizer.cs

@ -74,9 +74,7 @@ namespace ImageProcessorCore.Quantizers
/// <summary> /// <summary>
/// Override this to process the pixel in the second pass of the algorithm /// Override this to process the pixel in the second pass of the algorithm
/// </summary> /// </summary>
/// <param name="pixel"> /// <param name="pixel">The pixel to quantize</param>
/// The pixel to quantize
/// </param>
/// <returns> /// <returns>
/// The quantized value /// The quantized value
/// </returns> /// </returns>
@ -197,7 +195,7 @@ namespace ImageProcessorCore.Quantizers
/// </param> /// </param>
public void AddColor(TColor pixel) public void AddColor(TColor pixel)
{ {
TPacked packed = pixel.PackedValue; //.GetPackedValue(); TPacked packed = pixel.PackedValue;
// Check if this request is for the same color as the last // Check if this request is for the same color as the last
if (this.previousColor.Equals(packed)) if (this.previousColor.Equals(packed))
{ {
@ -224,11 +222,9 @@ namespace ImageProcessorCore.Quantizers
/// <summary> /// <summary>
/// Convert the nodes in the Octree to a palette with a maximum of colorCount colors /// Convert the nodes in the Octree to a palette with a maximum of colorCount colors
/// </summary> /// </summary>
/// <param name="colorCount"> /// <param name="colorCount">The maximum number of colors</param>
/// The maximum number of colors
/// </param>
/// <returns> /// <returns>
/// An <see cref="List{T}"/> with the palletized colors /// An <see cref="List{TColor}"/> with the palletized colors
/// </returns> /// </returns>
public List<TColor> Palletize(int colorCount) public List<TColor> Palletize(int colorCount)
{ {
@ -249,9 +245,7 @@ namespace ImageProcessorCore.Quantizers
/// <summary> /// <summary>
/// Get the palette index for the passed color /// Get the palette index for the passed color
/// </summary> /// </summary>
/// <param name="pixel"> /// <param name="pixel">The <see cref="TColor"/> containing the pixel data.</param>
/// The <see cref="T"/> containing the pixel data.
/// </param>
/// <returns> /// <returns>
/// The index of the given structure. /// The index of the given structure.
/// </returns> /// </returns>
@ -449,12 +443,8 @@ namespace ImageProcessorCore.Quantizers
/// <summary> /// <summary>
/// Traverse the tree, building up the color palette /// Traverse the tree, building up the color palette
/// </summary> /// </summary>
/// <param name="palette"> /// <param name="palette">The palette</param>
/// The palette /// <param name="index">The current palette index</param>
/// </param>
/// <param name="index">
/// The current palette index
/// </param>
public void ConstructPalette(List<TColor> palette, ref int index) public void ConstructPalette(List<TColor> palette, ref int index)
{ {
if (this.leaf) if (this.leaf)

6
src/ImageProcessorCore/Quantizers/Octree/Quantizer.cs

@ -11,6 +11,8 @@ namespace ImageProcessorCore.Quantizers
/// <summary> /// <summary>
/// Encapsulates methods to calculate the color palette of an image. /// Encapsulates methods to calculate the color palette of an image.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public abstract class Quantizer<TColor, TPacked> : IQuantizer<TColor, TPacked> public abstract class Quantizer<TColor, TPacked> : IQuantizer<TColor, TPacked>
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct
@ -98,10 +100,10 @@ namespace ImageProcessorCore.Quantizers
/// Execute a second pass through the bitmap /// Execute a second pass through the bitmap
/// </summary> /// </summary>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <param name="output">The outpuTColor pixel array</param> /// <param name="output">The output pixel array</param>
/// <param name="width">The width in pixels of the image</param> /// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height in pixels of the image</param> /// <param name="height">The height in pixels of the image</param>
protected virtual void SecondPass(PixelAccessor<TColor, TPacked> source, byte[] output, int width, int height) protected virtual void SecondPass(PixelAccessor<TColor,TPacked> source, byte[] output, int width, int height)
{ {
Parallel.For( Parallel.For(
0, 0,

4
src/ImageProcessorCore/Quantizers/Quantize.cs

@ -3,10 +3,10 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
using ImageProcessorCore.Quantizers;
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using ImageProcessorCore.Quantizers;
/// <summary> /// <summary>
/// Extension methods for the <see cref="Image{TColor, TPacked}"/> type. /// Extension methods for the <see cref="Image{TColor, TPacked}"/> type.
/// </summary> /// </summary>

4
src/ImageProcessorCore/Quantizers/QuantizedImage.cs

@ -14,8 +14,8 @@ namespace ImageProcessorCore.Quantizers
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public class QuantizedImage<TColor, TPacked> public class QuantizedImage<TColor, TPacked>
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="QuantizedImage{TColor, TPacked}"/> class. /// Initializes a new instance of the <see cref="QuantizedImage{TColor, TPacked}"/> class.

2
src/ImageProcessorCore/Quantizers/Wu/WuQuantizer.cs

@ -104,7 +104,7 @@ namespace ImageProcessorCore.Quantizers
private readonly byte[] tag; private readonly byte[] tag;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="WuQuantizer{TColor, TPacked}"/> class. /// Initializes a new instance of the <see cref="WuQuantizer{T,TP}"/> class.
/// </summary> /// </summary>
public WuQuantizer() public WuQuantizer()
{ {

31
src/ImageProcessorCore/Samplers/AutoOrient.cs

@ -17,7 +17,7 @@ namespace ImageProcessorCore
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image to crop.</param> /// <param name="source">The image to auto rotate.</param>
/// <returns>The <see cref="Image"/></returns> /// <returns>The <see cref="Image"/></returns>
public static Image<TColor, TPacked> AutoOrient<TColor, TPacked>(this Image<TColor, TPacked> source, ProgressEventHandler progressHandler = null) public static Image<TColor, TPacked> AutoOrient<TColor, TPacked>(this Image<TColor, TPacked> source, ProgressEventHandler progressHandler = null)
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
@ -27,11 +27,6 @@ namespace ImageProcessorCore
switch (orientation) switch (orientation)
{ {
case Orientation.Unknown:
case Orientation.TopLeft:
default:
return source;
case Orientation.TopRight: case Orientation.TopRight:
return source.Flip(FlipType.Horizontal, progressHandler); return source.Flip(FlipType.Horizontal, progressHandler);
@ -42,23 +37,33 @@ namespace ImageProcessorCore
return source.Flip(FlipType.Vertical, progressHandler); return source.Flip(FlipType.Vertical, progressHandler);
case Orientation.LeftTop: case Orientation.LeftTop:
return source return source.Rotate(RotateType.Rotate90, progressHandler)
.Rotate(RotateType.Rotate90, progressHandler) .Flip(FlipType.Horizontal, progressHandler);
.Flip(FlipType.Horizontal, progressHandler);
case Orientation.RightTop: case Orientation.RightTop:
return source.Rotate(RotateType.Rotate90, progressHandler); return source.Rotate(RotateType.Rotate90, progressHandler);
case Orientation.RightBottom: case Orientation.RightBottom:
return source return source.Flip(FlipType.Vertical, progressHandler)
.Flip(FlipType.Vertical, progressHandler) .Rotate(RotateType.Rotate270, progressHandler);
.Rotate(RotateType.Rotate270, progressHandler);
case Orientation.LeftBottom: case Orientation.LeftBottom:
return source.Rotate(RotateType.Rotate270, progressHandler); return source.Rotate(RotateType.Rotate270, progressHandler);
case Orientation.Unknown:
case Orientation.TopLeft:
default:
return source;
} }
} }
/// <summary>
/// Returns the current EXIF orientation
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The image to auto rotate.</param>
/// <returns>The <see cref="Orientation"/></returns>
private static Orientation GetExifOrientation<TColor, TPacked>(Image<TColor, TPacked> source) private static Orientation GetExifOrientation<TColor, TPacked>(Image<TColor, TPacked> source)
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct
@ -81,4 +86,4 @@ namespace ImageProcessorCore
return orientation; return orientation;
} }
} }
} }

18
src/ImageProcessorCore/Samplers/Options/ResizeHelper.cs

@ -17,7 +17,8 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Calculates the target location and bounds to perform the resize operation against. /// Calculates the target location and bounds to perform the resize operation against.
/// </summary> /// </summary>
/// <typeparam name="TColor">The type of pixels contained within the image.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <param name="options">The resize options.</param> /// <param name="options">The resize options.</param>
/// <returns> /// <returns>
@ -49,7 +50,8 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Calculates the target rectangle for crop mode. /// Calculates the target rectangle for crop mode.
/// </summary> /// </summary>
/// <typeparam name="TColor">The type of pixels contained within the image.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <param name="options">The resize options.</param> /// <param name="options">The resize options.</param>
/// <returns> /// <returns>
@ -169,7 +171,8 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Calculates the target rectangle for pad mode. /// Calculates the target rectangle for pad mode.
/// </summary> /// </summary>
/// <typeparam name="TColor">The type of pixels contained within the image.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <param name="options">The resize options.</param> /// <param name="options">The resize options.</param>
/// <returns> /// <returns>
@ -251,7 +254,8 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Calculates the target rectangle for box pad mode. /// Calculates the target rectangle for box pad mode.
/// </summary> /// </summary>
/// <typeparam name="TColor">The type of pixels contained within the image.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <param name="options">The resize options.</param> /// <param name="options">The resize options.</param>
/// <returns> /// <returns>
@ -339,7 +343,8 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Calculates the target rectangle for max mode. /// Calculates the target rectangle for max mode.
/// </summary> /// </summary>
/// <typeparam name="TColor">The type of pixels contained within the image.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <param name="options">The resize options.</param> /// <param name="options">The resize options.</param>
/// <returns> /// <returns>
@ -381,7 +386,8 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Calculates the target rectangle for min mode. /// Calculates the target rectangle for min mode.
/// </summary> /// </summary>
/// <typeparam name="TColor">The type of pixels contained within the image.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <param name="options">The resize options.</param> /// <param name="options">The resize options.</param>
/// <returns> /// <returns>

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

@ -27,7 +27,7 @@ namespace ImageProcessorCore.Processors
/// Initializes a new instance of the <see cref="EntropyCropProcessor{TColor, TPacked}"/> class. /// Initializes a new instance of the <see cref="EntropyCropProcessor{TColor, TPacked}"/> class.
/// </summary> /// </summary>
/// <param name="threshold">The threshold to split the image. Must be between 0 and 1.</param> /// <param name="threshold">The threshold to split the image. Must be between 0 and 1.</param>
/// <exception cref="ArgumentException"> /// <exception cref="System.ArgumentException">
/// <paramref name="threshold"/> is less than 0 or is greater than 1. /// <paramref name="threshold"/> is less than 0 or is greater than 1.
/// </exception> /// </exception>
public EntropyCropProcessor(float threshold) public EntropyCropProcessor(float threshold)
@ -55,7 +55,7 @@ namespace ImageProcessorCore.Processors
// Search for the first white pixels // Search for the first white pixels
Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0);
// Reset the targeTColor pixel to the correct size. // Reset the target pixel to the correct size.
target.SetPixels(rectangle.Width, rectangle.Height, new TColor[rectangle.Width * rectangle.Height]); target.SetPixels(rectangle.Width, rectangle.Height, new TColor[rectangle.Width * rectangle.Height]);
this.cropRectangle = rectangle; this.cropRectangle = rectangle;
} }

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

@ -16,4 +16,4 @@ namespace ImageProcessorCore.Processors
where TPacked : struct where TPacked : struct
{ {
} }
} }

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

@ -2,6 +2,9 @@
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
using System;
namespace ImageProcessorCore.Processors namespace ImageProcessorCore.Processors
{ {
using System.Numerics; using System.Numerics;
@ -34,7 +37,9 @@ namespace ImageProcessorCore.Processors
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle) protected override void OnApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle)
{ {
if (Angle == 0 || Angle == 90 || Angle == 180 || Angle == 270) const float Epsilon = .0001F;
if (Math.Abs(Angle) < Epsilon || Math.Abs(Angle - 90) < Epsilon || Math.Abs(Angle - 180) < Epsilon || Math.Abs(Angle - 270) < Epsilon)
{ {
return; return;
} }
@ -89,25 +94,26 @@ namespace ImageProcessorCore.Processors
/// <returns></returns> /// <returns></returns>
private bool OptimizedApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source) private bool OptimizedApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source)
{ {
if (Angle == 0) const float Epsilon = .0001F;
if (Math.Abs(Angle) < Epsilon)
{ {
target.ClonePixels(target.Width, target.Height, source.Pixels); target.ClonePixels(target.Width, target.Height, source.Pixels);
return true; return true;
} }
if (Angle == 90) if (Math.Abs(Angle - 90) < Epsilon)
{ {
this.Rotate90(target, source); this.Rotate90(target, source);
return true; return true;
} }
if (Angle == 180) if (Math.Abs(Angle - 180) < Epsilon)
{ {
this.Rotate180(target, source); this.Rotate180(target, source);
return true; return true;
} }
if (Angle == 270) if (Math.Abs(Angle - 270) < Epsilon)
{ {
this.Rotate270(target, source); this.Rotate270(target, source);
return true; return true;

2
src/ImageProcessorCore/Samplers/Resamplers/NearestNeighborResampler.cs

@ -7,7 +7,7 @@ namespace ImageProcessorCore
{ {
/// <summary> /// <summary>
/// The function implements the nearest neighbour algorithm. This uses an unscaled filter /// The function implements the nearest neighbour algorithm. This uses an unscaled filter
/// which will select the closesTColor pixel to the new pixels position. /// which will select the closest pixel to the new pixels position.
/// </summary> /// </summary>
public class NearestNeighborResampler : IResampler public class NearestNeighborResampler : IResampler
{ {

4
src/ImageProcessorCore/Samplers/RotateFlip.cs

@ -25,9 +25,7 @@ namespace ImageProcessorCore
where TColor : IPackedVector<TPacked> where TColor : IPackedVector<TPacked>
where TPacked : struct where TPacked : struct
{ {
return source return source.Rotate(rotateType, progressHandler).Flip(flipType, progressHandler);
.Rotate(rotateType, progressHandler)
.Flip(flipType, progressHandler);
} }
} }
} }

2
tests/ImageProcessorCore.Benchmarks/General/Copy.cs → tests/ImageProcessorCore.Benchmarks/General/ArrayCopy.cs

@ -5,7 +5,7 @@ namespace ImageProcessorCore.Benchmarks.General
{ {
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
public class Copy public class ArrayCopy
{ {
private double[] source = new double[10000]; private double[] source = new double[10000];

57
tests/ImageProcessorCore.Benchmarks/Image/CopyPixels.cs

@ -0,0 +1,57 @@
namespace ImageProcessorCore.Benchmarks.Image
{
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using CoreColor = ImageProcessorCore.Color;
using CoreImage = ImageProcessorCore.Image;
public class CopyPixels
{
[Benchmark(Description = "Copy by Pixel")]
public CoreColor CopyByPixel()
{
CoreImage source = new CoreImage(1024, 768);
CoreImage target = new CoreImage(1024, 768);
using (PixelAccessor<CoreColor, uint> sourcePixels = source.Lock())
using (PixelAccessor<CoreColor, uint> targetPixels = target.Lock())
{
Parallel.For(
0,
source.Height,
Bootstrapper.Instance.ParallelOptions,
y =>
{
for (int x = 0; x < source.Width; x++)
{
targetPixels[x, y] = sourcePixels[x, y];
}
});
return targetPixels[0, 0];
}
}
[Benchmark(Description = "Copy by Row")]
public CoreColor CopyByRow()
{
CoreImage source = new CoreImage(1024, 768);
CoreImage target = new CoreImage(1024, 768);
using (PixelAccessor<CoreColor, uint> sourcePixels = source.Lock())
using (PixelAccessor<CoreColor, uint> targetPixels = target.Lock())
{
Parallel.For(
0,
source.Height,
Bootstrapper.Instance.ParallelOptions,
y =>
{
sourcePixels.CopyRow(0, y, targetPixels, 0, y, source.Width);
});
return targetPixels[0, 0];
}
}
}
}

4
tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs

@ -12,7 +12,7 @@
[Benchmark(Baseline = true, Description = "System.Drawing Crop")] [Benchmark(Baseline = true, Description = "System.Drawing Crop")]
public Size CropSystemDrawing() public Size CropSystemDrawing()
{ {
using (Bitmap source = new Bitmap(400, 400)) using (Bitmap source = new Bitmap(800, 800))
{ {
using (Bitmap destination = new Bitmap(100, 100)) using (Bitmap destination = new Bitmap(100, 100))
{ {
@ -32,7 +32,7 @@
[Benchmark(Description = "ImageProcessorCore Crop")] [Benchmark(Description = "ImageProcessorCore Crop")]
public CoreSize CropResizeCore() public CoreSize CropResizeCore()
{ {
CoreImage image = new CoreImage(400, 400); CoreImage image = new CoreImage(800, 800);
image.Crop(100, 100); image.Crop(100, 100);
return new CoreSize(image.Width, image.Height); return new CoreSize(image.Width, image.Height);
} }

8
tests/ImageProcessorCore.Tests/Helpers/GuardTests.cs

@ -207,7 +207,7 @@ namespace ImageProcessorCore.Tests.Helpers
[Fact] [Fact]
public void IsTrueThrowsWhenArgIsFalse() public void IsTrueThrowsWhenArgIsFalse()
{ {
Assert.Throws<ArgumentException>(() => Guard.IsTrue(false, "foo")); Assert.Throws<ArgumentException>(() => Guard.IsTrue(false, "foo", "message"));
} }
/// <summary> /// <summary>
@ -216,7 +216,7 @@ namespace ImageProcessorCore.Tests.Helpers
[Fact] [Fact]
public void IsTrueDoesThrowsWhenArgIsTrue() public void IsTrueDoesThrowsWhenArgIsTrue()
{ {
Exception ex = Record.Exception(() => Guard.IsTrue(true, "foo")); Exception ex = Record.Exception(() => Guard.IsTrue(true, "foo", "message"));
Assert.Null(ex); Assert.Null(ex);
} }
@ -226,7 +226,7 @@ namespace ImageProcessorCore.Tests.Helpers
[Fact] [Fact]
public void IsFalseThrowsWhenArgIsFalse() public void IsFalseThrowsWhenArgIsFalse()
{ {
Assert.Throws<ArgumentException>(() => Guard.IsFalse(true, "foo")); Assert.Throws<ArgumentException>(() => Guard.IsFalse(true, "foo", "message"));
} }
/// <summary> /// <summary>
@ -235,7 +235,7 @@ namespace ImageProcessorCore.Tests.Helpers
[Fact] [Fact]
public void IsFalseDoesThrowsWhenArgIsTrue() public void IsFalseDoesThrowsWhenArgIsTrue()
{ {
Exception ex = Record.Exception(() => Guard.IsFalse(false, "foo")); Exception ex = Record.Exception(() => Guard.IsFalse(false, "foo", "message"));
Assert.Null(ex); Assert.Null(ex);
} }
} }

64
tests/ImageProcessorCore.Tests/Numerics/RationalTests.cs

@ -22,6 +22,7 @@ namespace ImageProcessorCore.Tests
Rational r2 = new Rational(3, 2); Rational r2 = new Rational(3, 2);
Assert.Equal(r1, r2); Assert.Equal(r1, r2);
Assert.True(r1 == r2);
Rational r3 = new Rational(7.55); Rational r3 = new Rational(7.55);
Rational r4 = new Rational(755, 100); Rational r4 = new Rational(755, 100);
@ -41,6 +42,7 @@ namespace ImageProcessorCore.Tests
Rational second = new Rational(100, 100); Rational second = new Rational(100, 100);
Assert.NotEqual(first, second); Assert.NotEqual(first, second);
Assert.True(first != second);
} }
/// <summary> /// <summary>
@ -49,9 +51,65 @@ namespace ImageProcessorCore.Tests
[Fact] [Fact]
public void ConstructorAssignsProperties() public void ConstructorAssignsProperties()
{ {
Rational first = new Rational(4, 5); Rational rational = new Rational(7, 55);
Assert.Equal(4, first.Numerator); Assert.Equal(7U, rational.Numerator);
Assert.Equal(5, first.Denominator); Assert.Equal(55U, rational.Denominator);
rational = new Rational(755, 100);
Assert.Equal(151U, rational.Numerator);
Assert.Equal(20U, rational.Denominator);
rational = new Rational(755, 100, false);
Assert.Equal(755U, rational.Numerator);
Assert.Equal(100U, rational.Denominator);
rational = new Rational(-7.55);
Assert.Equal(151U, rational.Numerator);
Assert.Equal(20U, rational.Denominator);
rational = new Rational(7);
Assert.Equal(7U, rational.Numerator);
Assert.Equal(1U, rational.Denominator);
}
[Fact]
public void Fraction()
{
Rational first = new Rational(1.0 / 1600);
Rational second = new Rational(1.0 / 1600, true);
Assert.False(first.Equals(second));
}
[Fact]
public void ToDouble()
{
Rational rational = new Rational(0, 0);
Assert.Equal(double.NaN, rational.ToDouble());
rational = new Rational(2, 0);
Assert.Equal(double.PositiveInfinity, rational.ToDouble());
}
[Fact]
public void ToStringRepresention()
{
Rational rational = new Rational(0, 0);
Assert.Equal("[ Indeterminate ]", rational.ToString());
rational = new Rational(double.PositiveInfinity);
Assert.Equal("[ PositiveInfinity ]", rational.ToString());
rational = new Rational(double.NegativeInfinity);
Assert.Equal("[ PositiveInfinity ]", rational.ToString());
rational = new Rational(0, 1);
Assert.Equal("0", rational.ToString());
rational = new Rational(2, 1);
Assert.Equal("2", rational.ToString());
rational = new Rational(1, 2);
Assert.Equal("1/2", rational.ToString());
} }
} }
} }

122
tests/ImageProcessorCore.Tests/Numerics/SignedRationalTests.cs

@ -0,0 +1,122 @@
// <copyright file="RationalTests.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Tests
{
using Xunit;
/// <summary>
/// Tests the <see cref="SignedRational"/> struct.
/// </summary>
public class SignedRationalTests
{
/// <summary>
/// Tests the equality operators for equality.
/// </summary>
[Fact]
public void AreEqual()
{
SignedRational r1 = new SignedRational(3, 2);
SignedRational r2 = new SignedRational(3, 2);
Assert.Equal(r1, r2);
Assert.True(r1 == r2);
SignedRational r3 = new SignedRational(7.55);
SignedRational r4 = new SignedRational(755, 100);
SignedRational r5 = new SignedRational(151, 20);
Assert.Equal(r3, r4);
Assert.Equal(r4, r5);
}
/// <summary>
/// Tests the equality operators for inequality.
/// </summary>
[Fact]
public void AreNotEqual()
{
SignedRational first = new SignedRational(0, 100);
SignedRational second = new SignedRational(100, 100);
Assert.NotEqual(first, second);
Assert.True(first != second);
}
/// <summary>
/// Tests whether the Rational constructor correctly assign properties.
/// </summary>
[Fact]
public void ConstructorAssignsProperties()
{
SignedRational rational = new SignedRational(7, -55);
Assert.Equal(7, rational.Numerator);
Assert.Equal(-55, rational.Denominator);
rational = new SignedRational(-755, 100);
Assert.Equal(-151, rational.Numerator);
Assert.Equal(20, rational.Denominator);
rational = new SignedRational(-755, -100, false);
Assert.Equal(-755, rational.Numerator);
Assert.Equal(-100, rational.Denominator);
rational = new SignedRational(-151, -20);
Assert.Equal(-151, rational.Numerator);
Assert.Equal(-20, rational.Denominator);
rational = new SignedRational(-7.55);
Assert.Equal(-151, rational.Numerator);
Assert.Equal(20, rational.Denominator);
rational = new SignedRational(7);
Assert.Equal(7, rational.Numerator);
Assert.Equal(1, rational.Denominator);
}
[Fact]
public void Fraction()
{
SignedRational first = new SignedRational(1.0 / 1600);
SignedRational second = new SignedRational(1.0 / 1600, true);
Assert.False(first.Equals(second));
}
[Fact]
public void ToDouble()
{
SignedRational rational = new SignedRational(0, 0);
Assert.Equal(double.NaN, rational.ToDouble());
rational = new SignedRational(2, 0);
Assert.Equal(double.PositiveInfinity, rational.ToDouble());
rational = new SignedRational(-2, 0);
Assert.Equal(double.NegativeInfinity, rational.ToDouble());
}
[Fact]
public void ToStringRepresention()
{
SignedRational rational = new SignedRational(0, 0);
Assert.Equal("[ Indeterminate ]", rational.ToString());
rational = new SignedRational(double.PositiveInfinity);
Assert.Equal("[ PositiveInfinity ]", rational.ToString());
rational = new SignedRational(double.NegativeInfinity);
Assert.Equal("[ NegativeInfinity ]", rational.ToString());
rational = new SignedRational(0, 1);
Assert.Equal("0", rational.ToString());
rational = new SignedRational(2, 1);
Assert.Equal("2", rational.ToString());
rational = new SignedRational(1, 2);
Assert.Equal("1/2", rational.ToString());
}
}
}

110
tests/ImageProcessorCore.Tests/Profiles/Exif/ExifProfileTests.cs

@ -64,12 +64,11 @@ namespace ImageProcessorCore.Tests
{ {
using (MemoryStream memStream = new MemoryStream()) using (MemoryStream memStream = new MemoryStream())
{ {
// double exposureTime = 1.0 / 1600; double exposureTime = 1.0 / 1600;
Rational exposureTime = new Rational(1, 1600);
ExifProfile profile = GetExifProfile(); ExifProfile profile = GetExifProfile();
profile.SetValue(ExifTag.ExposureTime, exposureTime); profile.SetValue(ExifTag.ExposureTime, new Rational(exposureTime));
Image image = new Image(1, 1); Image image = new Image(1, 1);
image.ExifProfile = profile; image.ExifProfile = profile;
@ -84,12 +83,12 @@ namespace ImageProcessorCore.Tests
ExifValue value = profile.GetValue(ExifTag.ExposureTime); ExifValue value = profile.GetValue(ExifTag.ExposureTime);
Assert.NotNull(value); Assert.NotNull(value);
Assert.Equal(exposureTime, value.Value); Assert.NotEqual(exposureTime, ((Rational)value.Value).ToDouble());
memStream.Position = 0; memStream.Position = 0;
profile = GetExifProfile(); profile = GetExifProfile();
profile.SetValue(ExifTag.ExposureTime, exposureTime); profile.SetValue(ExifTag.ExposureTime, new Rational(exposureTime, true));
image.ExifProfile = profile; image.ExifProfile = profile;
image.SaveAsJpeg(memStream); image.SaveAsJpeg(memStream);
@ -101,7 +100,7 @@ namespace ImageProcessorCore.Tests
Assert.NotNull(profile); Assert.NotNull(profile);
value = profile.GetValue(ExifTag.ExposureTime); value = profile.GetValue(ExifTag.ExposureTime);
TestValue(value, exposureTime); Assert.Equal(exposureTime, ((Rational)value.Value).ToDouble());
} }
} }
@ -111,104 +110,29 @@ namespace ImageProcessorCore.Tests
using (FileStream stream = File.OpenRead(TestImages.Jpg.Floorplan)) using (FileStream stream = File.OpenRead(TestImages.Jpg.Floorplan))
{ {
Image image = new Image(stream); Image image = new Image(stream);
image.ExifProfile.SetValue(ExifTag.ExposureBiasValue, Rational.PositiveInfinity); image.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.PositiveInfinity));
image = WriteAndRead(image); image = WriteAndRead(image);
ExifValue value = image.ExifProfile.GetValue(ExifTag.ExposureBiasValue); ExifValue value = image.ExifProfile.GetValue(ExifTag.ExposureBiasValue);
Assert.NotNull(value); Assert.NotNull(value);
Assert.Equal(Rational.PositiveInfinity, value.Value); Assert.Equal(new SignedRational(double.PositiveInfinity), value.Value);
image.ExifProfile.SetValue(ExifTag.ExposureBiasValue, Rational.NegativeInfinity); image.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.NegativeInfinity));
image = WriteAndRead(image); image = WriteAndRead(image);
value = image.ExifProfile.GetValue(ExifTag.ExposureBiasValue); value = image.ExifProfile.GetValue(ExifTag.ExposureBiasValue);
Assert.NotNull(value); Assert.NotNull(value);
Assert.Equal(Rational.NegativeInfinity, value.Value); Assert.Equal(new SignedRational(double.NegativeInfinity), value.Value);
image.ExifProfile.SetValue(ExifTag.FlashEnergy, Rational.NegativeInfinity); image.ExifProfile.SetValue(ExifTag.FlashEnergy, new Rational(double.NegativeInfinity));
image = WriteAndRead(image); image = WriteAndRead(image);
value = image.ExifProfile.GetValue(ExifTag.FlashEnergy); value = image.ExifProfile.GetValue(ExifTag.FlashEnergy);
Assert.NotNull(value); Assert.NotNull(value);
Assert.Equal(Rational.PositiveInfinity, value.Value); Assert.Equal(new Rational(double.PositiveInfinity), value.Value);
} }
} }
//[Fact]
//public void WriteFraction()
//{
// using (MemoryStream memStream = new MemoryStream())
// {
// double exposureTime = 1.0 / 1600;
// ExifProfile profile = GetExifProfile();
// profile.SetValue(ExifTag.ExposureTime, exposureTime);
// Image image = new Image(1, 1);
// image.ExifProfile = profile;
// image.SaveAsJpeg(memStream);
// memStream.Position = 0;
// image = new Image(memStream);
// profile = image.ExifProfile;
// Assert.NotNull(profile);
// ExifValue value = profile.GetValue(ExifTag.ExposureTime);
// Assert.NotNull(value);
// Assert.NotEqual(exposureTime, value.Value);
// memStream.Position = 0;
// profile = GetExifProfile();
// profile.SetValue(ExifTag.ExposureTime, exposureTime);
// profile.BestPrecision = true;
// image.ExifProfile = profile;
// image.SaveAsJpeg(memStream);
// memStream.Position = 0;
// image = new Image(memStream);
// profile = image.ExifProfile;
// Assert.NotNull(profile);
// value = profile.GetValue(ExifTag.ExposureTime);
// TestValue(value, exposureTime);
// }
//}
//[Fact]
//public void ReadWriteInfinity()
//{
// using (FileStream stream = File.OpenRead(TestImages.Jpg.Floorplan))
// {
// Image image = new Image(stream);
// image.ExifProfile.SetValue(ExifTag.ExposureBiasValue, double.PositiveInfinity);
// image = WriteAndRead(image);
// ExifValue value = image.ExifProfile.GetValue(ExifTag.ExposureBiasValue);
// Assert.NotNull(value);
// Assert.Equal(double.PositiveInfinity, value.Value);
// image.ExifProfile.SetValue(ExifTag.ExposureBiasValue, double.NegativeInfinity);
// image = WriteAndRead(image);
// value = image.ExifProfile.GetValue(ExifTag.ExposureBiasValue);
// Assert.NotNull(value);
// Assert.Equal(double.NegativeInfinity, value.Value);
// image.ExifProfile.SetValue(ExifTag.FlashEnergy, double.NegativeInfinity);
// image = WriteAndRead(image);
// value = image.ExifProfile.GetValue(ExifTag.FlashEnergy);
// Assert.NotNull(value);
// Assert.Equal(double.PositiveInfinity, value.Value);
// }
//}
[Fact] [Fact]
public void SetValue() public void SetValue()
{ {
@ -224,11 +148,11 @@ namespace ImageProcessorCore.Tests
Assert.Throws<ArgumentException>(() => { value.Value = 15; }); Assert.Throws<ArgumentException>(() => { value.Value = 15; });
image.ExifProfile.SetValue(ExifTag.ShutterSpeedValue, new Rational(75.55)); image.ExifProfile.SetValue(ExifTag.ShutterSpeedValue, new SignedRational(75.55));
value = image.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); value = image.ExifProfile.GetValue(ExifTag.ShutterSpeedValue);
TestValue(value, new Rational(7555, 100)); TestValue(value, new SignedRational(7555, 100));
Assert.Throws<ArgumentException>(() => { value.Value = 75; }); Assert.Throws<ArgumentException>(() => { value.Value = 75; });
@ -258,7 +182,7 @@ namespace ImageProcessorCore.Tests
TestValue(value, "ImageProcessorCore"); TestValue(value, "ImageProcessorCore");
value = image.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); value = image.ExifProfile.GetValue(ExifTag.ShutterSpeedValue);
TestValue(value, new Rational(75.55)); TestValue(value, new SignedRational(75.55));
value = image.ExifProfile.GetValue(ExifTag.XResolution); value = image.ExifProfile.GetValue(ExifTag.XResolution);
TestValue(value, new Rational(150.0)); TestValue(value, new Rational(150.0));
@ -372,6 +296,12 @@ namespace ImageProcessorCore.Tests
Assert.Equal(expected, value.Value); Assert.Equal(expected, value.Value);
} }
private static void TestValue(ExifValue value, SignedRational expected)
{
Assert.NotNull(value);
Assert.Equal(expected, value.Value);
}
private static void TestValue(ExifValue value, Rational[] expected) private static void TestValue(ExifValue value, Rational[] expected)
{ {
Assert.NotNull(value); Assert.NotNull(value);

Loading…
Cancel
Save