From 88d55a3afd48021d470cd100503af474b2d938bf Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 24 Aug 2016 09:24:01 +1000 Subject: [PATCH 1/7] Faster pixel access [skip ci] Former-commit-id: 6acf8d3a4de0287b22205537591bdf3517f4fc32 Former-commit-id: 11a91852b54bf2e42182cfe6bf08a0fdb969a84f Former-commit-id: 05574a79bfe33a798d9dfff869e712a276e8ee64 --- src/ImageProcessorCore/Image/PixelAccessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageProcessorCore/Image/PixelAccessor.cs b/src/ImageProcessorCore/Image/PixelAccessor.cs index 833a4c678..1c5227055 100644 --- a/src/ImageProcessorCore/Image/PixelAccessor.cs +++ b/src/ImageProcessorCore/Image/PixelAccessor.cs @@ -104,8 +104,8 @@ namespace ImageProcessorCore /// The at the specified position. public TColor this[int x, int y] { - get { return Unsafe.Read(pixelsBase + ((y * this.RowStride) + (x * this.PixelSize))); } - set { Unsafe.Write(pixelsBase + ((y * this.RowStride) + (x * this.PixelSize)), value); } + get { return Unsafe.Read(this.pixelsBase + (y * this.Width + x) * Unsafe.SizeOf()); } + set { Unsafe.Write(this.pixelsBase + (y * this.Width + x) * Unsafe.SizeOf(), value); } } /// From 510d349c1281f0275f14396aab495e092f2445cc Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 24 Aug 2016 13:05:16 +1000 Subject: [PATCH 2/7] Benchmark CopyRow touch #457 [skip ci] Former-commit-id: 3ebb26faca837aeac974a67da676be409d902ae7 Former-commit-id: d7a8a733fabfb6621f8f3a04c56b9f2aa6913392 Former-commit-id: ec769799df83b3f7b1d4934aead8e6ae1e280736 --- src/ImageProcessorCore/Image/PixelAccessor.cs | 19 +++++++ .../General/{Copy.cs => ArrayCopy.cs} | 2 +- .../Image/CopyPixels.cs | 57 +++++++++++++++++++ .../Samplers/Crop.cs | 4 +- 4 files changed, 79 insertions(+), 3 deletions(-) rename tests/ImageProcessorCore.Benchmarks/General/{Copy.cs => ArrayCopy.cs} (96%) create mode 100644 tests/ImageProcessorCore.Benchmarks/Image/CopyPixels.cs diff --git a/src/ImageProcessorCore/Image/PixelAccessor.cs b/src/ImageProcessorCore/Image/PixelAccessor.cs index 1c5227055..d7146e4aa 100644 --- a/src/ImageProcessorCore/Image/PixelAccessor.cs +++ b/src/ImageProcessorCore/Image/PixelAccessor.cs @@ -108,6 +108,25 @@ namespace ImageProcessorCore set { Unsafe.Write(this.pixelsBase + (y * this.Width + x) * Unsafe.SizeOf(), value); } } + /// + /// Copies an entire row of pixels. + /// + /// The x-coordinate of the source row. + /// The y-coordinate of the source row. + /// The target pixel buffer accessor. + /// The x-coordinate of the target row. + /// The y-coordinate of the target row. + /// The number of pixels to copy + public void CopyRow(int sourceX, int sourceY, PixelAccessor target, int targetX, int targetY, int pixelCount) + { + int size = Unsafe.SizeOf(); + 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); + } + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// diff --git a/tests/ImageProcessorCore.Benchmarks/General/Copy.cs b/tests/ImageProcessorCore.Benchmarks/General/ArrayCopy.cs similarity index 96% rename from tests/ImageProcessorCore.Benchmarks/General/Copy.cs rename to tests/ImageProcessorCore.Benchmarks/General/ArrayCopy.cs index de4dadffb..349c94fa3 100644 --- a/tests/ImageProcessorCore.Benchmarks/General/Copy.cs +++ b/tests/ImageProcessorCore.Benchmarks/General/ArrayCopy.cs @@ -5,7 +5,7 @@ namespace ImageProcessorCore.Benchmarks.General { using BenchmarkDotNet.Attributes; - public class Copy + public class ArrayCopy { private double[] source = new double[10000]; diff --git a/tests/ImageProcessorCore.Benchmarks/Image/CopyPixels.cs b/tests/ImageProcessorCore.Benchmarks/Image/CopyPixels.cs new file mode 100644 index 000000000..df0706bc6 --- /dev/null +++ b/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 sourcePixels = source.Lock()) + using (PixelAccessor 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 sourcePixels = source.Lock()) + using (PixelAccessor 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]; + } + } + } +} diff --git a/tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs b/tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs index 4eaeea6ec..ef7cb9722 100644 --- a/tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs +++ b/tests/ImageProcessorCore.Benchmarks/Samplers/Crop.cs @@ -12,7 +12,7 @@ [Benchmark(Baseline = true, Description = "System.Drawing Crop")] public Size CropSystemDrawing() { - using (Bitmap source = new Bitmap(400, 400)) + using (Bitmap source = new Bitmap(800, 800)) { using (Bitmap destination = new Bitmap(100, 100)) { @@ -32,7 +32,7 @@ [Benchmark(Description = "ImageProcessorCore Crop")] public CoreSize CropResizeCore() { - CoreImage image = new CoreImage(400, 400); + CoreImage image = new CoreImage(800, 800); image.Crop(100, 100); return new CoreSize(image.Width, image.Height); } From ccc13bbac32ef71cbed9178367481e88b705443a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 25 Aug 2016 11:10:39 +1000 Subject: [PATCH 3/7] Revert Unsafe.Copy and cleanup Former-commit-id: 401fa40681eb231eabcfe11f5115c05404c84498 Former-commit-id: 160012def06309092c8ee00cba8fa99a53f83ebf Former-commit-id: bf644e6660e40cb25e20de9058bc181284730409 --- src/ImageProcessorCore/Bootstrapper.cs | 5 +- .../Colors/Colorspaces/IAlmostEquatable.cs | 7 +- .../Common/Extensions/ByteExtensions.cs | 2 +- .../Common/Helpers/Guard.cs | 77 +++++-------------- src/ImageProcessorCore/Filters/Blend.cs | 6 +- src/ImageProcessorCore/Filters/BoxBlur.cs | 8 +- src/ImageProcessorCore/Filters/DetectEdges.cs | 4 +- src/ImageProcessorCore/Filters/Invert.cs | 8 +- src/ImageProcessorCore/Filters/Kodachrome.cs | 2 + src/ImageProcessorCore/Filters/Pixelate.cs | 6 +- .../Filters/Processors/AlphaProcessor.cs | 2 +- .../Processors/BackgroundColorProcessor.cs | 2 +- .../Binarization/BinaryThresholdProcessor.cs | 4 +- .../Filters/Processors/BlendProcessor.cs | 2 +- .../Filters/Processors/BrightnessProcessor.cs | 2 +- .../Processors/ColorMatrix/HueProcessor.cs | 13 +--- .../ColorMatrix/IColorMatrixFilter.cs | 2 + .../ColorMatrix/PolaroidProcessor.cs | 2 +- .../ColorMatrix/SaturationProcessor.cs | 24 ++---- .../Processors/ColorMatrix/SepiaProcessor.cs | 2 +- .../Filters/Processors/ContrastProcessor.cs | 8 +- .../Convolution/BoxBlurProcessor.cs | 2 +- .../Convolution/ConvolutionFilter.cs | 2 + .../EdgeDetection/EdgeDetector2DFilter.cs | 2 + .../EdgeDetection/EdgeDetectorFilter.cs | 2 + .../EdgeDetection/IEdgeDetectorFilter.cs | 2 + .../Convolution/GuassianBlurProcessor.cs | 6 +- .../Convolution/GuassianSharpenProcessor.cs | 6 +- .../Filters/Processors/GlowProcessor.cs | 6 +- .../Filters/Processors/PixelateProcessor.cs | 4 +- .../Filters/Processors/VignetteProcessor.cs | 3 +- src/ImageProcessorCore/Filters/Sepia.cs | 2 +- .../Formats/Bmp/BmpDecoder.cs | 4 +- .../Formats/Bmp/BmpDecoderCore.cs | 11 ++- .../Formats/Bmp/BmpEncoderCore.cs | 15 ++-- .../Formats/Gif/GifDecoderCore.cs | 2 +- .../Formats/Gif/LzwDecoder.cs | 1 - .../Formats/Gif/LzwEncoder.cs | 2 +- .../Sections/GifGraphicsControlExtension.cs | 2 +- .../Formats/IImageEncoder.cs | 4 +- .../Jpg/JpegDecoderCore.cs.REMOVED.git-id | 2 +- .../Formats/Jpg/JpegEncoderCore.cs | 10 +-- .../Formats/Png/Filters/UpFilter.cs | 2 +- .../Formats/Png/PngDecoder.cs | 2 + .../Formats/Png/PngDecoderCore.cs | 13 ++-- .../Formats/Png/PngEncoder.cs | 4 +- .../IO/EndianBitConverter.cs | 2 +- src/ImageProcessorCore/Image/IImageBase.cs | 12 ++- .../Image/IImageProcessor.cs | 4 +- src/ImageProcessorCore/Image/ImageBase.cs | 53 ++++--------- .../Image/ImageExtensions.cs | 20 ++--- src/ImageProcessorCore/Image/PixelAccessor.cs | 3 +- src/ImageProcessorCore/ImageProcessor.cs | 4 +- .../Profiles/Exif/ExifProfile.cs | 2 +- .../Quantizers/IQuantizer.cs | 2 +- .../Quantizers/Octree/OctreeQuantizer.cs | 32 +++----- .../Quantizers/Octree/Quantizer.cs | 6 +- src/ImageProcessorCore/Quantizers/Quantize.cs | 4 +- .../Quantizers/QuantizedImage.cs | 4 +- .../Quantizers/Wu/WuQuantizer.cs | 2 +- src/ImageProcessorCore/Samplers/AutoOrient.cs | 31 ++++---- .../Samplers/Options/ResizeHelper.cs | 18 +++-- .../Processors/EntropyCropProcessor.cs | 4 +- .../Samplers/Processors/ImageSampler.cs | 2 +- .../Samplers/Processors/RotateProcessor.cs | 16 ++-- .../Resamplers/NearestNeighborResampler.cs | 2 +- src/ImageProcessorCore/Samplers/RotateFlip.cs | 4 +- 67 files changed, 235 insertions(+), 291 deletions(-) diff --git a/src/ImageProcessorCore/Bootstrapper.cs b/src/ImageProcessorCore/Bootstrapper.cs index 9e5ec39fd..2ddb64dcd 100644 --- a/src/ImageProcessorCore/Bootstrapper.cs +++ b/src/ImageProcessorCore/Bootstrapper.cs @@ -8,7 +8,6 @@ namespace ImageProcessorCore using System; using System.Collections.Generic; using System.Collections.ObjectModel; - using System.Reflection; using System.Threading.Tasks; using Formats; @@ -51,8 +50,8 @@ namespace ImageProcessorCore /// /// Gets the collection of supported /// - public IReadOnlyCollection ImageFormats => - new ReadOnlyCollection(this.imageFormats); + public IReadOnlyCollection ImageFormats => new ReadOnlyCollection(this.imageFormats); + /// /// Gets or sets the global parallel options for processing tasks in parallel. diff --git a/src/ImageProcessorCore/Colors/Colorspaces/IAlmostEquatable.cs b/src/ImageProcessorCore/Colors/Colorspaces/IAlmostEquatable.cs index 97f57c21a..134b19014 100644 --- a/src/ImageProcessorCore/Colors/Colorspaces/IAlmostEquatable.cs +++ b/src/ImageProcessorCore/Colors/Colorspaces/IAlmostEquatable.cs @@ -12,8 +12,9 @@ namespace ImageProcessorCore /// a type-specific method for determining approximate equality of instances. /// /// The type of objects to compare. - /// The object specifying the type to specify precision with. - public interface IAlmostEquatable where TPacked : struct, IComparable + /// The object specifying the type to specify precision with. + public interface IAlmostEquatable + where TPrecision : struct, IComparable { /// /// Indicates whether the current object is equal to another object of the same type @@ -24,6 +25,6 @@ namespace ImageProcessorCore /// /// true if the current object is equal to the other parameter; otherwise, false. /// - bool AlmostEquals(TColor other, TPacked precision); + bool AlmostEquals(TColor other, TPrecision precision); } } diff --git a/src/ImageProcessorCore/Common/Extensions/ByteExtensions.cs b/src/ImageProcessorCore/Common/Extensions/ByteExtensions.cs index bee549c0f..f5da2252b 100644 --- a/src/ImageProcessorCore/Common/Extensions/ByteExtensions.cs +++ b/src/ImageProcessorCore/Common/Extensions/ByteExtensions.cs @@ -20,7 +20,7 @@ namespace ImageProcessorCore /// The number of bits per value. /// The resulting array. Is never null. /// is null. - /// is less than or equals than zero. + /// is less than or equals than zero. public static byte[] ToArrayByBitsLength(this byte[] bytes, int bits) { Guard.NotNull(bytes, "bytes"); diff --git a/src/ImageProcessorCore/Common/Helpers/Guard.cs b/src/ImageProcessorCore/Common/Helpers/Guard.cs index 6940a460e..4e8c764f7 100644 --- a/src/ImageProcessorCore/Common/Helpers/Guard.cs +++ b/src/ImageProcessorCore/Common/Helpers/Guard.cs @@ -1,16 +1,8 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. // -// -// Provides methods to protect against invalid parameters. -// -// -------------------------------------------------------------------------------------------------------------------- -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("ImageProcessorCore.Tests")] namespace ImageProcessorCore { using System; @@ -26,18 +18,10 @@ namespace ImageProcessorCore /// Verifies, that the method parameter with specified object value is not null /// and throws an exception if it is found to be so. /// - /// - /// The target object, which cannot be null. - /// - /// - /// The name of the parameter that is to be checked. - /// - /// - /// The error message, if any to add to the exception. - /// - /// - /// is null - /// + /// The target object, which cannot be null. + /// The name of the parameter that is to be checked. + /// The error message, if any to add to the exception. + /// is null public static void NotNull(object target, string parameterName, string message = "") { if (target == null) @@ -58,13 +42,8 @@ namespace ImageProcessorCore /// /// The target string, which should be checked against being null or empty. /// Name of the parameter. - /// - /// is null. - /// - /// - /// is - /// empty or contains only blanks. - /// + /// is null. + /// is empty or contains only blanks. public static void NotNullOrEmpty(string target, string parameterName) { if (target == null) @@ -94,9 +73,7 @@ namespace ImageProcessorCore { if (value.CompareTo(max) >= 0) { - throw new ArgumentOutOfRangeException( - parameterName, - $"Value must be less than {max}."); + throw new ArgumentOutOfRangeException(parameterName, $"Value must be less than {max}."); } } @@ -116,9 +93,7 @@ namespace ImageProcessorCore { if (value.CompareTo(max) > 0) { - throw new ArgumentOutOfRangeException( - parameterName, - $"Value must be less than or equal to {max}."); + throw new ArgumentOutOfRangeException(parameterName, $"Value must be less than or equal to {max}."); } } @@ -134,7 +109,7 @@ namespace ImageProcessorCore /// is less than the minimum value. /// public static void MustBeGreaterThan(TValue value, TValue min, string parameterName) - where TValue : IComparable + where TValue : IComparable { if (value.CompareTo(min) <= 0) { @@ -156,13 +131,11 @@ namespace ImageProcessorCore /// is less than the minimum value. /// public static void MustBeGreaterThanOrEqualTo(TValue value, TValue min, string parameterName) - where TValue : IComparable + where TValue : IComparable { if (value.CompareTo(min) < 0) { - throw new ArgumentOutOfRangeException( - parameterName, - $"Value must be greater than or equal to {min}."); + throw new ArgumentOutOfRangeException(parameterName, $"Value must be greater than or equal to {min}."); } } @@ -175,17 +148,15 @@ namespace ImageProcessorCore /// The maximum value. /// The name of the parameter that is to be checked. /// The type of the value. - /// + /// /// is less than the minimum value of greater than the maximum value. /// public static void MustBeBetweenOrEqualTo(TValue value, TValue min, TValue max, string parameterName) - where TValue : IComparable + where TValue : IComparable { if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0) { - throw new ArgumentOutOfRangeException( - parameterName, - $"Value must be greater than or equal to {min} and less than or equal to {max}."); + throw new ArgumentOutOfRangeException(parameterName, $"Value must be greater than or equal to {min} and less than or equal to {max}."); } } @@ -202,7 +173,7 @@ namespace ImageProcessorCore /// /// The error message, if any to add to the exception. /// - /// + /// /// is null /// public static void IsTrue(bool target, string parameterName, string message = "") @@ -222,15 +193,9 @@ namespace ImageProcessorCore /// Verifies, that the method parameter with specified target value is false /// and throws an exception if it is found to be so. /// - /// - /// The target value, which cannot be true. - /// - /// - /// The name of the parameter that is to be checked. - /// - /// - /// The error message, if any to add to the exception. - /// + /// The target value, which cannot be true. + /// The name of the parameter that is to be checked. + /// The error message, if any to add to the exception. /// /// is null /// diff --git a/src/ImageProcessorCore/Filters/Blend.cs b/src/ImageProcessorCore/Filters/Blend.cs index 77b65cf20..215bd23eb 100644 --- a/src/ImageProcessorCore/Filters/Blend.cs +++ b/src/ImageProcessorCore/Filters/Blend.cs @@ -1,7 +1,7 @@ // // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. -// ------------------------------------------------------------------------------------------------------------------- +// namespace ImageProcessorCore { @@ -15,10 +15,10 @@ namespace ImageProcessorCore /// /// Combines the given image together with the current one by blending their pixels. /// - /// The image this method extends. - /// The image to blend with the currently processing image. /// The pixel format. /// The packed format. uint, long, float. + /// The image this method extends. + /// The image to blend with the currently processing image. /// The opacity of the image image to blend. Must be between 0 and 100. /// A delegate which is called as progress is made processing the image. /// The . diff --git a/src/ImageProcessorCore/Filters/BoxBlur.cs b/src/ImageProcessorCore/Filters/BoxBlur.cs index acbd0d895..f4a0ce238 100644 --- a/src/ImageProcessorCore/Filters/BoxBlur.cs +++ b/src/ImageProcessorCore/Filters/BoxBlur.cs @@ -15,8 +15,8 @@ namespace ImageProcessorCore /// /// Applies a box blur to the image. /// - /// The pixel format. - /// The packed format. uint, long, float. + /// The pixel format. + /// The packed format. long, float. /// The image this method extends. /// The 'radius' value representing the size of the area to sample. /// A delegate which is called as progress is made processing the image. @@ -31,8 +31,8 @@ namespace ImageProcessorCore /// /// Applies a box blur to the image. /// - /// The pixel format. - /// The packed format. uint, long, float. + /// The pixel format. + /// The packed format. long, float. /// The image this method extends. /// The 'radius' value representing the size of the area to sample. /// diff --git a/src/ImageProcessorCore/Filters/DetectEdges.cs b/src/ImageProcessorCore/Filters/DetectEdges.cs index 9e906fcf7..60006ea72 100644 --- a/src/ImageProcessorCore/Filters/DetectEdges.cs +++ b/src/ImageProcessorCore/Filters/DetectEdges.cs @@ -29,7 +29,7 @@ namespace ImageProcessorCore } /// - /// Detects any edges within the image. Uses the filter + /// Detects any edges within the image. Uses the filter /// operating in Grayscale mode. /// /// The pixel format. @@ -118,7 +118,7 @@ namespace ImageProcessorCore break; default: - processor = new ScharrProcessor { Grayscale = grayscale }; + processor = new SobelProcessor { Grayscale = grayscale }; break; } diff --git a/src/ImageProcessorCore/Filters/Invert.cs b/src/ImageProcessorCore/Filters/Invert.cs index e1f59d8d3..ed7124662 100644 --- a/src/ImageProcessorCore/Filters/Invert.cs +++ b/src/ImageProcessorCore/Filters/Invert.cs @@ -1,20 +1,22 @@ // // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. -// ------------------------------------------------------------------------------------------------------------------- +// namespace ImageProcessorCore { using Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Inverts the colors of the image. /// + /// The pixel format. + /// The packed format. uint, long, float. /// The image this method extends. /// A delegate which is called as progress is made processing the image. /// The . @@ -28,6 +30,8 @@ namespace ImageProcessorCore /// /// Inverts the colors of the image. /// + /// The pixel format. + /// The packed format. uint, long, float. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. diff --git a/src/ImageProcessorCore/Filters/Kodachrome.cs b/src/ImageProcessorCore/Filters/Kodachrome.cs index 9a27f2010..7310703b0 100644 --- a/src/ImageProcessorCore/Filters/Kodachrome.cs +++ b/src/ImageProcessorCore/Filters/Kodachrome.cs @@ -10,6 +10,8 @@ namespace ImageProcessorCore /// /// Extension methods for the type. /// + /// The pixel format. + /// The packed format. uint, long, float. public static partial class ImageExtensions { /// diff --git a/src/ImageProcessorCore/Filters/Pixelate.cs b/src/ImageProcessorCore/Filters/Pixelate.cs index 32b443881..a9b7f63af 100644 --- a/src/ImageProcessorCore/Filters/Pixelate.cs +++ b/src/ImageProcessorCore/Filters/Pixelate.cs @@ -11,13 +11,13 @@ namespace ImageProcessorCore /// /// Extension methods for the type. /// - /// The pixel format. - /// The packed format. uint, long, float. public static partial class ImageExtensions { /// /// Pixelates and image with the given pixel size. /// + /// The pixel format. + /// The packed format. uint, long, float. /// The image this method extends. /// The size of the pixels. /// A delegate which is called as progress is made processing the image. @@ -32,6 +32,8 @@ namespace ImageProcessorCore /// /// Pixelates and image with the given pixel size. /// + /// The pixel format. + /// The packed format. uint, long, float. /// The image this method extends. /// The size of the pixels. /// diff --git a/src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs b/src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs index 2b04b6a33..9572cf63b 100644 --- a/src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/AlphaProcessor.cs @@ -22,7 +22,7 @@ namespace ImageProcessorCore.Processors /// Initializes a new instance of the class. /// /// The percentage to adjust the opacity of the image. Must be between 0 and 100. - /// + /// /// is less than 0 or is greater than 100. /// public AlphaProcessor(int percent) diff --git a/src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs b/src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs index b5a22e796..cb3173385 100644 --- a/src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/BackgroundColorProcessor.cs @@ -24,7 +24,7 @@ namespace ImageProcessorCore.Processors /// /// Initializes a new instance of the class. /// - /// The to set the background color to. + /// The to set the background color to. public BackgroundColorProcessor(TColor color) { this.Value = color; diff --git a/src/ImageProcessorCore/Filters/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Binarization/BinaryThresholdProcessor.cs index bb6845878..4b4f67b50 100644 --- a/src/ImageProcessorCore/Filters/Processors/Binarization/BinaryThresholdProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/Binarization/BinaryThresholdProcessor.cs @@ -22,7 +22,7 @@ namespace ImageProcessorCore.Processors /// Initializes a new instance of the class. /// /// The threshold to split the image. Must be between 0 and 1. - /// + /// /// is less than 0 or is greater than 1. /// public BinaryThresholdProcessor(float threshold) @@ -111,4 +111,4 @@ namespace ImageProcessorCore.Processors } } } -} \ No newline at end of file +} diff --git a/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs b/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs index 1cf51c1d1..1b333b6f0 100644 --- a/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs @@ -24,7 +24,7 @@ namespace ImageProcessorCore.Processors private readonly ImageBase blend; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The image to blend with the currently processing image. diff --git a/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs b/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs index f1259dac5..f87e514e9 100644 --- a/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/BrightnessProcessor.cs @@ -22,7 +22,7 @@ namespace ImageProcessorCore.Processors /// Initializes a new instance of the class. /// /// The new brightness of the image. Must be between -100 and 100. - /// + /// /// is less than -100 or is greater than 100. /// public BrightnessProcessor(int brightness) diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/HueProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/HueProcessor.cs index a497f5bd4..afe6dbca9 100644 --- a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/HueProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/HueProcessor.cs @@ -9,7 +9,7 @@ namespace ImageProcessorCore.Processors using System.Numerics; /// - /// An to change the hue of an . + /// An to change the hue of an . /// /// The pixel format. /// The packed format. uint, long, float. @@ -18,12 +18,7 @@ namespace ImageProcessorCore.Processors where TPacked : struct { /// - /// The used to alter the image. - /// - private Matrix4x4 matrix; - - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The new brightness of the image. Must be between -100 and 100. public HueProcessor(float angle) @@ -67,7 +62,7 @@ namespace ImageProcessorCore.Processors M33 = (float)(lumB + (cosradians * oneMinusLumB) + (sinradians * lumB)) }; - this.matrix = matrix4X4; + this.Matrix = matrix4X4; } /// @@ -76,7 +71,7 @@ namespace ImageProcessorCore.Processors public float Angle { get; } /// - public override Matrix4x4 Matrix => this.matrix; + public override Matrix4x4 Matrix { get; } /// public override bool Compand => false; diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/IColorMatrixFilter.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/IColorMatrixFilter.cs index cb4c3c36a..f1f6377ef 100644 --- a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/IColorMatrixFilter.cs +++ b/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 /// alter the image pixels. /// + /// The pixel format. + /// The packed format. uint, long, float. public interface IColorMatrixFilter : IImageProcessor where TColor : IPackedVector where TPacked : struct diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs index 956896c64..c737a7bb6 100644 --- a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/PolaroidProcessor.cs @@ -45,4 +45,4 @@ namespace ImageProcessorCore.Processors new GlowProcessor { GlowColor = packedG, Radius = target.Width / 4F }.Apply(target, target, sourceRectangle); } } -} +} \ No newline at end of file diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SaturationProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SaturationProcessor.cs index ae031fed9..37612357a 100644 --- a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SaturationProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SaturationProcessor.cs @@ -8,7 +8,7 @@ namespace ImageProcessorCore.Processors using System.Numerics; /// - /// An to change the saturation of an . + /// An to change the saturation of an . /// /// The pixel format. /// The packed format. uint, long, float. @@ -17,28 +17,16 @@ namespace ImageProcessorCore.Processors where TPacked : struct { /// - /// The saturation to be applied to the image. - /// - private readonly int saturation; - - /// - /// The used to alter the image. - /// - private Matrix4x4 matrix; - - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The new saturation of the image. Must be between -100 and 100. - /// + /// /// is less than -100 or is greater than 100. /// public SaturationProcessor(int saturation) { Guard.MustBeBetweenOrEqualTo(saturation, -100, 100, nameof(saturation)); - this.saturation = saturation; - - float saturationFactor = this.saturation / 100f; + float saturationFactor = saturation / 100f; // Stop at -1 to prevent inversion. saturationFactor++; @@ -65,10 +53,10 @@ namespace ImageProcessorCore.Processors M33 = saturationComplementB + saturationFactor, }; - this.matrix = matrix4X4; + this.Matrix = matrix4X4; } /// - public override Matrix4x4 Matrix => this.matrix; + public override Matrix4x4 Matrix { get; } } } diff --git a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SepiaProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SepiaProcessor.cs index 6a4b2c880..040d48884 100644 --- a/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SepiaProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/ColorMatrix/SepiaProcessor.cs @@ -34,4 +34,4 @@ namespace ImageProcessorCore.Processors /// public override bool Compand => false; } -} +} \ No newline at end of file diff --git a/src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs b/src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs index 91a7e4a12..54a9e10cc 100644 --- a/src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/ContrastProcessor.cs @@ -12,17 +12,17 @@ namespace ImageProcessorCore.Processors /// /// An to change the contrast of an . /// - /// The pixel format. - /// The packed format. uint, long, float. + /// The pixel format. + /// The packed format. long, float. public class ContrastProcessor : ImageProcessor where TColor : IPackedVector where TPacked : struct { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The new contrast of the image. Must be between -100 and 100. - /// + /// /// is less than -100 or is greater than 100. /// public ContrastProcessor(int contrast) diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs index f8c2d9109..1b8430f5f 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/BoxBlurProcessor.cs @@ -30,7 +30,7 @@ namespace ImageProcessorCore.Processors private float[,] kernelX; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs index dfc026bae..da0c11df5 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/ConvolutionFilter.cs @@ -11,6 +11,8 @@ namespace ImageProcessorCore.Processors /// /// Defines a filter that uses a 2 dimensional matrix to perform convolution against an image. /// + /// The pixel format. + /// The packed format. uint, long, float. public abstract class ConvolutionFilter : ImageProcessor where TColor : IPackedVector where TPacked : struct diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetector2DFilter.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetector2DFilter.cs index 6475ad7cb..34a589630 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetector2DFilter.cs +++ b/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 /// one-dimensional matrices. /// + /// The pixel format. + /// The packed format. uint, long, float. public abstract class EdgeDetector2DFilter : Convolution2DFilter, IEdgeDetectorFilter where TColor : IPackedVector where TPacked : struct diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetectorFilter.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetectorFilter.cs index 2d221664d..04cd2cddd 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/EdgeDetectorFilter.cs +++ b/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 /// two dimensional matrix. /// + /// The pixel format. + /// The packed format. uint, long, float. public abstract class EdgeDetectorFilter : ConvolutionFilter, IEdgeDetectorFilter where TColor : IPackedVector where TPacked : struct diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs index e101612b6..620db328e 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/EdgeDetection/IEdgeDetectorFilter.cs @@ -8,6 +8,8 @@ namespace ImageProcessorCore.Processors /// /// Provides properties and methods allowing the detection of edges within an image. /// + /// The pixel format. + /// The packed format. uint, long, float. public interface IEdgeDetectorFilter : IImageProcessor, IEdgeDetectorFilter where TColor : IPackedVector where TPacked : struct diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs index 7ab5fe4fb..7a9b0137f 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianBlurProcessor.cs @@ -37,7 +37,7 @@ namespace ImageProcessorCore.Processors private float[,] kernelX; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The 'sigma' value representing the weight of the blur. public GuassianBlurProcessor(float sigma = 3f) @@ -47,7 +47,7 @@ namespace ImageProcessorCore.Processors } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. @@ -59,7 +59,7 @@ namespace ImageProcessorCore.Processors } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'sigma' value representing the weight of the blur. diff --git a/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs b/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs index 2c290f2df..18704faf1 100644 --- a/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/Convolution/GuassianSharpenProcessor.cs @@ -37,7 +37,7 @@ namespace ImageProcessorCore.Processors private float[,] kernelX; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'sigma' value representing the weight of the sharpening. @@ -49,7 +49,7 @@ namespace ImageProcessorCore.Processors } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. @@ -61,7 +61,7 @@ namespace ImageProcessorCore.Processors } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'sigma' value representing the weight of the sharpen. diff --git a/src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs b/src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs index ee6b07d17..10a68ffcf 100644 --- a/src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/GlowProcessor.cs @@ -15,11 +15,11 @@ namespace ImageProcessorCore.Processors /// The pixel format. /// The packed format. uint, long, float. public class GlowProcessor : ImageProcessor - where TColor : IPackedVector - where TPacked : struct + where TColor : IPackedVector + where TPacked : struct { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public GlowProcessor() { diff --git a/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs b/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs index 4e5c63ed4..300081050 100644 --- a/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs @@ -19,7 +19,7 @@ namespace ImageProcessorCore.Processors where TPacked : struct { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The size of the pixels. Must be greater than 0. /// @@ -110,4 +110,4 @@ namespace ImageProcessorCore.Processors } } } -} +} \ No newline at end of file diff --git a/src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs b/src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs index c6e8cf35e..142f20e6b 100644 --- a/src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs +++ b/src/ImageProcessorCore/Filters/Processors/VignetteProcessor.cs @@ -96,5 +96,4 @@ namespace ImageProcessorCore.Processors } } } -} - +} \ No newline at end of file diff --git a/src/ImageProcessorCore/Filters/Sepia.cs b/src/ImageProcessorCore/Filters/Sepia.cs index 750d3016f..126c0d92a 100644 --- a/src/ImageProcessorCore/Filters/Sepia.cs +++ b/src/ImageProcessorCore/Filters/Sepia.cs @@ -55,4 +55,4 @@ namespace ImageProcessorCore } } } -} +} \ No newline at end of file diff --git a/src/ImageProcessorCore/Formats/Bmp/BmpDecoder.cs b/src/ImageProcessorCore/Formats/Bmp/BmpDecoder.cs index c87de4cc0..2f2484554 100644 --- a/src/ImageProcessorCore/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageProcessorCore/Formats/Bmp/BmpDecoder.cs @@ -71,8 +71,8 @@ namespace ImageProcessorCore.Formats /// public void Decode(Image image, Stream stream) - where TColor : IPackedVector - where TPacked : struct + where TColor : IPackedVector + where TPacked : struct { Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); diff --git a/src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs b/src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs index 82d7823f4..9aa612b80 100644 --- a/src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageProcessorCore/Formats/Bmp/BmpDecoderCore.cs @@ -128,8 +128,7 @@ namespace ImageProcessorCore.Formats case BmpCompression.RGB: if (this.infoHeader.HeaderSize != 40) { - throw new ImageFormatException( - $"Header Size value '{this.infoHeader.HeaderSize}' is not valid."); + throw new ImageFormatException($"Header Size value '{this.infoHeader.HeaderSize}' is not valid."); } if (this.infoHeader.BitsPerPixel == 32) @@ -190,7 +189,7 @@ namespace ImageProcessorCore.Formats /// /// The pixel format. /// The packed format. uint, long, float. - /// The image data to assign the palette to. + /// The image data to assign the palette to. /// The containing the colors. /// The width of the bitmap. /// The height of the bitmap. @@ -255,7 +254,7 @@ namespace ImageProcessorCore.Formats /// /// The pixel format. /// The packed format. uint, long, float. - /// The image data to assign the palette to. + /// The image data to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. /// Whether the bitmap is inverted. @@ -306,7 +305,7 @@ namespace ImageProcessorCore.Formats /// /// The pixel format. /// The packed format. uint, long, float. - /// The image data to assign the palette to. + /// The image data to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. /// Whether the bitmap is inverted. @@ -347,7 +346,7 @@ namespace ImageProcessorCore.Formats /// /// The pixel format. /// The packed format. uint, long, float. - /// The image data to assign the palette to. + /// The image data to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. /// Whether the bitmap is inverted. diff --git a/src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs b/src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs index 5b959d844..d296d3241 100644 --- a/src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageProcessorCore/Formats/Bmp/BmpEncoderCore.cs @@ -27,8 +27,8 @@ namespace ImageProcessorCore.Formats /// /// Encodes the image to the specified stream from the . /// - /// The pixel format. - /// The packed format. uint, long, float. + /// The pixel format. + /// The packed format. long, float. /// The to encode from. /// The to encode the image data to. /// The @@ -134,11 +134,11 @@ namespace ImageProcessorCore.Formats switch (this.bmpBitsPerPixel) { case BmpBitsPerPixel.Pixel32: - this.Write32Bit(writer, pixels); + this.Write32Bit(writer, pixels); break; case BmpBitsPerPixel.Pixel24: - this.Write24Bit(writer, pixels); + this.Write24Bit(writer, pixels); break; } } @@ -150,7 +150,7 @@ namespace ImageProcessorCore.Formats /// The pixel format. /// The packed format. uint, long, float. /// The containing the stream to write to. - /// The containing pixel data. + /// The containing pixel data. private void Write32Bit(EndianBinaryWriter writer, PixelAccessor pixels) where TColor : IPackedVector where TPacked : struct @@ -176,8 +176,9 @@ namespace ImageProcessorCore.Formats /// Writes the 24bit color palette to the stream. /// /// The pixel format. - /// The packed format. uint, long, float./// The containing the stream to write to. - /// The containing pixel data. + /// The packed format. uint, long, float. + /// The containing the stream to write to. + /// The containing pixel data. private void Write24Bit(EndianBinaryWriter writer, PixelAccessor pixels) where TColor : IPackedVector where TPacked : struct diff --git a/src/ImageProcessorCore/Formats/Gif/GifDecoderCore.cs b/src/ImageProcessorCore/Formats/Gif/GifDecoderCore.cs index d37bca929..766e5bb33 100644 --- a/src/ImageProcessorCore/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageProcessorCore/Formats/Gif/GifDecoderCore.cs @@ -12,7 +12,7 @@ namespace ImageProcessorCore.Formats /// Performs the gif decoding operation. /// /// The pixel format. - /// The packed format. uint, long, float. + /// The packed format. uint, long, float. internal class GifDecoderCore where TColor : IPackedVector where TPacked : struct diff --git a/src/ImageProcessorCore/Formats/Gif/LzwDecoder.cs b/src/ImageProcessorCore/Formats/Gif/LzwDecoder.cs index 1474cc708..7135ca62e 100644 --- a/src/ImageProcessorCore/Formats/Gif/LzwDecoder.cs +++ b/src/ImageProcessorCore/Formats/Gif/LzwDecoder.cs @@ -5,7 +5,6 @@ namespace ImageProcessorCore.Formats { - using System; using System.IO; /// diff --git a/src/ImageProcessorCore/Formats/Gif/LzwEncoder.cs b/src/ImageProcessorCore/Formats/Gif/LzwEncoder.cs index 469cacbce..a9681d2c5 100644 --- a/src/ImageProcessorCore/Formats/Gif/LzwEncoder.cs +++ b/src/ImageProcessorCore/Formats/Gif/LzwEncoder.cs @@ -310,7 +310,7 @@ namespace ImageProcessorCore.Formats } /// - /// Return the nexTColor pixel from the image + /// Return the next pixel from the image /// /// /// The diff --git a/src/ImageProcessorCore/Formats/Gif/Sections/GifGraphicsControlExtension.cs b/src/ImageProcessorCore/Formats/Gif/Sections/GifGraphicsControlExtension.cs index 59dbceb21..071dc62c8 100644 --- a/src/ImageProcessorCore/Formats/Gif/Sections/GifGraphicsControlExtension.cs +++ b/src/ImageProcessorCore/Formats/Gif/Sections/GifGraphicsControlExtension.cs @@ -27,7 +27,7 @@ namespace ImageProcessorCore.Formats /// /// Gets or sets the transparency index. /// 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. /// public int TransparencyIndex { get; set; } diff --git a/src/ImageProcessorCore/Formats/IImageEncoder.cs b/src/ImageProcessorCore/Formats/IImageEncoder.cs index 808371456..445b7f645 100644 --- a/src/ImageProcessorCore/Formats/IImageEncoder.cs +++ b/src/ImageProcessorCore/Formats/IImageEncoder.cs @@ -38,11 +38,11 @@ namespace ImageProcessorCore.Formats bool IsSupportedFileExtension(string extension); /// - /// Encodes the image to the specified stream from the . + /// Encodes the image to the specified stream from the . /// /// The pixel format. /// The packed format. uint, long, float. - /// The to encode from. + /// The to encode from. /// The to encode the image data to. void Encode(Image image, Stream stream) where TColor : IPackedVector diff --git a/src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id b/src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id index bd65b8702..2fb48cb7e 100644 --- a/src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id +++ b/src/ImageProcessorCore/Formats/Jpg/JpegDecoderCore.cs.REMOVED.git-id @@ -1 +1 @@ -d6ce5dd6236ac6ef9ba570fb4e57e81c06bbb854 \ No newline at end of file +457ce2b70d772a8f3b259b2d624fcb41c75357e1 \ No newline at end of file diff --git a/src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs b/src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs index 783406a28..ef6df20ab 100644 --- a/src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs +++ b/src/ImageProcessorCore/Formats/Jpg/JpegEncoderCore.cs @@ -504,7 +504,7 @@ namespace ImageProcessorCore.Formats // Write the image data. using (PixelAccessor pixels = image.Lock()) { - this.WriteSOS(pixels); + this.WriteSOS(pixels); } // Write the End Of Image marker. @@ -720,10 +720,10 @@ namespace ImageProcessorCore.Formats switch (this.subsample) { case JpegSubsample.Ratio444: - this.Encode444(pixels); + this.Encode444(pixels); break; case JpegSubsample.Ratio420: - this.Encode420(pixels); + this.Encode420(pixels); break; } @@ -750,7 +750,7 @@ namespace ImageProcessorCore.Formats { for (int x = 0; x < pixels.Width; x += 8) { - this.ToYCbCr(pixels, x, y, b, cb, cr); + this.ToYCbCr(pixels, x, y, b, cb, cr); prevDCY = this.WriteBlock(b, QuantIndex.Luminance, prevDCY); prevDCCb = this.WriteBlock(cb, QuantIndex.Chrominance, prevDCCb); prevDCCr = this.WriteBlock(cr, QuantIndex.Chrominance, prevDCCr); @@ -784,7 +784,7 @@ namespace ImageProcessorCore.Formats int xOff = (i & 1) * 8; int yOff = (i & 2) * 4; - this.ToYCbCr(pixels, x + xOff, y + yOff, b, cb[i], cr[i]); + this.ToYCbCr(pixels, x + xOff, y + yOff, b, cb[i], cr[i]); prevDCY = this.WriteBlock(b, QuantIndex.Luminance, prevDCY); } diff --git a/src/ImageProcessorCore/Formats/Png/Filters/UpFilter.cs b/src/ImageProcessorCore/Formats/Png/Filters/UpFilter.cs index deb4d9d0d..8f87ec1ca 100644 --- a/src/ImageProcessorCore/Formats/Png/Filters/UpFilter.cs +++ b/src/ImageProcessorCore/Formats/Png/Filters/UpFilter.cs @@ -6,7 +6,7 @@ namespace ImageProcessorCore.Formats { /// - /// 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. /// /// diff --git a/src/ImageProcessorCore/Formats/Png/PngDecoder.cs b/src/ImageProcessorCore/Formats/Png/PngDecoder.cs index 264971e4d..0fad54dd7 100644 --- a/src/ImageProcessorCore/Formats/Png/PngDecoder.cs +++ b/src/ImageProcessorCore/Formats/Png/PngDecoder.cs @@ -77,6 +77,8 @@ namespace ImageProcessorCore.Formats /// /// Decodes the image from the specified stream to the . /// + /// The pixel format. + /// The packed format. uint, long, float. /// The to decode to. /// The containing image data. public void Decode(Image image, Stream stream) diff --git a/src/ImageProcessorCore/Formats/Png/PngDecoderCore.cs b/src/ImageProcessorCore/Formats/Png/PngDecoderCore.cs index 3cf8be40e..ab27ccd17 100644 --- a/src/ImageProcessorCore/Formats/Png/PngDecoderCore.cs +++ b/src/ImageProcessorCore/Formats/Png/PngDecoderCore.cs @@ -81,13 +81,13 @@ namespace ImageProcessorCore.Formats /// Decodes the stream to the image. /// /// The pixel format. - /// The packed format. uint, long, float. + /// The packed format. uint, long, float. /// The image to decode to. /// The stream containing image data. /// /// Thrown if the stream does not contain and end chunk. /// - /// + /// /// Thrown if the image is larger than the maximum allowable size. /// public void Decode(Image image, Stream stream) @@ -150,11 +150,7 @@ namespace ImageProcessorCore.Formats } TColor[] pixels = new TColor[this.header.Width * this.header.Height]; - - this.ReadScanlines(dataStream, pixels); - - image.SetPixels(this.header.Width, this.header.Height, pixels); } } @@ -226,9 +222,10 @@ namespace ImageProcessorCore.Formats /// /// Reads the scanlines within the image. /// + /// The pixel format. + /// The packed format. uint, long, float. /// The containing data. - /// - /// The containing pixel data. + /// The pixel data. private void ReadScanlines(MemoryStream dataStream, TColor[] pixels) where TColor : IPackedVector where TPacked : struct diff --git a/src/ImageProcessorCore/Formats/Png/PngEncoder.cs b/src/ImageProcessorCore/Formats/Png/PngEncoder.cs index 660315c58..75f686857 100644 --- a/src/ImageProcessorCore/Formats/Png/PngEncoder.cs +++ b/src/ImageProcessorCore/Formats/Png/PngEncoder.cs @@ -73,8 +73,8 @@ namespace ImageProcessorCore.Formats /// public void Encode(Image image, Stream stream) - where TColor : IPackedVector - where TPacked : struct + where TColor : IPackedVector + where TPacked : struct { PngEncoderCore encoder = new PngEncoderCore { diff --git a/src/ImageProcessorCore/IO/EndianBitConverter.cs b/src/ImageProcessorCore/IO/EndianBitConverter.cs index 71cba1a51..107148100 100644 --- a/src/ImageProcessorCore/IO/EndianBitConverter.cs +++ b/src/ImageProcessorCore/IO/EndianBitConverter.cs @@ -246,7 +246,7 @@ namespace ImageProcessorCore.IO /// The start index passed in /// The number of bytes required /// value is a null reference - /// + /// /// startIndex is less than zero or greater than the length of value minus bytesRequired. /// [SuppressMessage("ReSharper", "UnusedParameter.Local", Justification = "Keeps code DRY")] diff --git a/src/ImageProcessorCore/Image/IImageBase.cs b/src/ImageProcessorCore/Image/IImageBase.cs index 1afd7daaf..cc2ef9a79 100644 --- a/src/ImageProcessorCore/Image/IImageBase.cs +++ b/src/ImageProcessorCore/Image/IImageBase.cs @@ -5,8 +5,6 @@ namespace ImageProcessorCore { - using System; - /// /// Encapsulates the basic properties and methods required to manipulate images in varying formats. /// @@ -27,10 +25,10 @@ namespace ImageProcessorCore /// The new width of the image. Must be greater than zero. /// The new height of the image. Must be greater than zero. /// The array with pixels. Must be a multiple of the width and height. - /// + /// /// Thrown if either or are less than or equal to 0. /// - /// + /// /// Thrown if the length is not equal to Width * Height. /// void SetPixels(int width, int height, TColor[] pixels); @@ -42,10 +40,10 @@ namespace ImageProcessorCore /// The new width of the image. Must be greater than zero. /// The new height of the image. Must be greater than zero. /// The array with pixels. Must be a multiple of four times the width and height. - /// + /// /// Thrown if either or are less than or equal to 0. /// - /// + /// /// Thrown if the length is not equal to Width * Height. /// 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. /// /// - /// The + /// The PixelAccessor Lock(); } diff --git a/src/ImageProcessorCore/Image/IImageProcessor.cs b/src/ImageProcessorCore/Image/IImageProcessor.cs index cf9f2c15d..06f0514d8 100644 --- a/src/ImageProcessorCore/Image/IImageProcessor.cs +++ b/src/ImageProcessorCore/Image/IImageProcessor.cs @@ -44,7 +44,7 @@ namespace ImageProcessorCore.Processors bool Compand { get; set; } /// - /// Applies the process to the specified portion of the specified . + /// Applies the process to the specified portion of the specified . /// /// Target image to apply the process to. /// The source image. Cannot be null. @@ -64,7 +64,7 @@ namespace ImageProcessorCore.Processors void Apply(ImageBase target, ImageBase source, Rectangle sourceRectangle); /// - /// Applies the process to the specified portion of the specified at the specified + /// Applies the process to the specified portion of the specified at the specified /// location and with the specified size. /// /// Target image to apply the process to. diff --git a/src/ImageProcessorCore/Image/ImageBase.cs b/src/ImageProcessorCore/Image/ImageBase.cs index 9f4b36323..4d17d9f01 100644 --- a/src/ImageProcessorCore/Image/ImageBase.cs +++ b/src/ImageProcessorCore/Image/ImageBase.cs @@ -3,8 +3,6 @@ // Licensed under the Apache License, Version 2.0. // -using System.Runtime.CompilerServices; - namespace ImageProcessorCore { using System; @@ -12,20 +10,15 @@ namespace ImageProcessorCore /// /// 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. /// /// The pixel format. /// The packed format. uint, long, float. [DebuggerDisplay("Image: {Width}x{Height}")] - public abstract unsafe class ImageBase : IImageBase + public abstract class ImageBase : IImageBase where TColor : IPackedVector where TPacked : struct { - /// - /// The image pixels - /// - private TColor[] pixelBuffer; - /// /// Initializes a new instance of the class. /// @@ -38,7 +31,7 @@ namespace ImageProcessorCore /// /// The width of the image in pixels. /// The height of the image in pixels. - /// + /// /// Thrown if either or are less than or equal to 0. /// protected ImageBase(int width, int height) @@ -48,7 +41,7 @@ namespace ImageProcessorCore this.Width = width; this.Height = height; - this.pixelBuffer = new TColor[width * height]; + this.Pixels = new TColor[width * height]; } /// @@ -68,9 +61,9 @@ namespace ImageProcessorCore this.Height = other.Height; this.CopyProperties(other); - // Copy the pixels. - this.pixelBuffer = new TColor[this.Width * this.Height]; - Unsafe.Copy(Unsafe.AsPointer(ref this.pixelBuffer), ref other.pixelBuffer); + // Copy the pixels. Don't use Unsafe.Copy as it is breaking edge detection. + this.Pixels = new TColor[this.Width * this.Height]; + Array.Copy(other.Pixels, this.Pixels, other.Pixels.Length); } /// @@ -80,7 +73,7 @@ namespace ImageProcessorCore public int MaxHeight { get; set; } = int.MaxValue; /// - public TColor[] Pixels => this.pixelBuffer; + public TColor[] Pixels { get; private set; } /// public int Width { get; private set; } @@ -103,15 +96,8 @@ namespace ImageProcessorCore /// public void SetPixels(int width, int height, TColor[] pixels) { - if (width <= 0) - { - 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."); - } + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); if (pixels.Length != width * height) { @@ -120,21 +106,14 @@ namespace ImageProcessorCore this.Width = width; this.Height = height; - this.pixelBuffer = pixels; + this.Pixels = pixels; } /// public void ClonePixels(int width, int height, TColor[] pixels) { - if (width <= 0) - { - 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."); - } + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); if (pixels.Length != width * height) { @@ -144,9 +123,9 @@ namespace ImageProcessorCore this.Width = width; this.Height = height; - // Copy the pixels. - this.pixelBuffer = new TColor[pixels.Length]; - Unsafe.Copy(Unsafe.AsPointer(ref this.pixelBuffer), ref pixels); + // Copy the pixels. Don't use Unsafe.Copy as it is breaking edge detection. + this.Pixels = new TColor[pixels.Length]; + Array.Copy(pixels, this.Pixels, pixels.Length); } /// diff --git a/src/ImageProcessorCore/Image/ImageExtensions.cs b/src/ImageProcessorCore/Image/ImageExtensions.cs index 476c2a48e..103f8c3eb 100644 --- a/src/ImageProcessorCore/Image/ImageExtensions.cs +++ b/src/ImageProcessorCore/Image/ImageExtensions.cs @@ -20,7 +20,7 @@ namespace ImageProcessorCore /// Saves the image to the given stream with the bmp format. /// /// The pixel format. - /// The packed format. uint, long, float. + /// The packed format. uint, long, float. /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. @@ -34,7 +34,7 @@ namespace ImageProcessorCore /// Saves the image to the given stream with the png format. /// /// The pixel format. - /// The packed format. uint, long, float. + /// The packed format. uint, long, float. /// The image this method extends. /// The stream to save the image to. /// 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. /// /// The pixel format. - /// The packed format. uint, long, float. + /// The packed format. uint, long, float. /// The image this method extends. /// The stream to save the image to. /// The quality to save the image to. Between 1 and 100. @@ -64,7 +64,7 @@ namespace ImageProcessorCore /// Saves the image to the given stream with the gif format. /// /// The pixel format. - /// The packed format. uint, long, float. + /// The packed format. uint, long, float. /// The image this method extends. /// The stream to save the image to. /// The quality to save the image to representing the number of colors. Between 1 and 256. @@ -115,8 +115,8 @@ namespace ImageProcessorCore /// This method is not chainable. /// /// - /// The pixel format. - /// The packed format. uint, long, float. + /// The pixel format. + /// The packed format. long, float. /// The source image. Cannot be null. /// The target image width. /// The target image height. @@ -135,8 +135,8 @@ namespace ImageProcessorCore /// This method does will resize the target image if the source and target rectangles are different. /// /// - /// The pixel format. - /// The packed format. uint, long, float. + /// The pixel format. + /// The packed format. long, float. /// The source image. Cannot be null. /// The target image width. /// The target image height. @@ -159,8 +159,8 @@ namespace ImageProcessorCore /// /// Performs the given action on the source image. /// - /// The pixel format. - /// The packed format. uint, long, float. + /// The pixel format. + /// The packed format. long, float. /// The image to perform the action against. /// Whether to clone the image. /// The to perform against the image. diff --git a/src/ImageProcessorCore/Image/PixelAccessor.cs b/src/ImageProcessorCore/Image/PixelAccessor.cs index d7146e4aa..d6f15047e 100644 --- a/src/ImageProcessorCore/Image/PixelAccessor.cs +++ b/src/ImageProcessorCore/Image/PixelAccessor.cs @@ -14,7 +14,7 @@ namespace ImageProcessorCore /// /// The pixel format. /// The packed format. uint, long, float. - public sealed unsafe class PixelAccessor : IDisposable + public unsafe class PixelAccessor : IDisposable where TColor : IPackedVector where TPacked : struct { @@ -106,6 +106,7 @@ namespace ImageProcessorCore { get { return Unsafe.Read(this.pixelsBase + (y * this.Width + x) * Unsafe.SizeOf()); } set { Unsafe.Write(this.pixelsBase + (y * this.Width + x) * Unsafe.SizeOf(), value); } + } /// diff --git a/src/ImageProcessorCore/ImageProcessor.cs b/src/ImageProcessorCore/ImageProcessor.cs index 2ed0e56b2..10014e0f3 100644 --- a/src/ImageProcessorCore/ImageProcessor.cs +++ b/src/ImageProcessorCore/ImageProcessor.cs @@ -12,6 +12,8 @@ namespace ImageProcessorCore.Processors /// /// Allows the application of processors to images. /// + /// The pixel format. + /// The packed format. uint, long, float. public abstract class ImageProcessor : IImageProcessor where TColor : IPackedVector where TPacked : struct @@ -93,8 +95,6 @@ namespace ImageProcessorCore.Processors /// /// This method is called before the process is applied to prepare the processor. /// - /// The pixel format. - /// The packed format. uint, long, float. /// Target image to apply the process to. /// The source image. Cannot be null. /// diff --git a/src/ImageProcessorCore/Profiles/Exif/ExifProfile.cs b/src/ImageProcessorCore/Profiles/Exif/ExifProfile.cs index 638d721a7..2c1509fd0 100644 --- a/src/ImageProcessorCore/Profiles/Exif/ExifProfile.cs +++ b/src/ImageProcessorCore/Profiles/Exif/ExifProfile.cs @@ -2,9 +2,9 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // + namespace ImageProcessorCore { - using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; diff --git a/src/ImageProcessorCore/Quantizers/IQuantizer.cs b/src/ImageProcessorCore/Quantizers/IQuantizer.cs index af4733b11..e0087a52e 100644 --- a/src/ImageProcessorCore/Quantizers/IQuantizer.cs +++ b/src/ImageProcessorCore/Quantizers/IQuantizer.cs @@ -15,7 +15,7 @@ namespace ImageProcessorCore.Quantizers where TPacked : struct { /// - /// Quantize an image and return the resulting outpuTColor pixels. + /// Quantize an image and return the resulting output pixels. /// /// The image to quantize. /// The maximum number of colors to return. diff --git a/src/ImageProcessorCore/Quantizers/Octree/OctreeQuantizer.cs b/src/ImageProcessorCore/Quantizers/Octree/OctreeQuantizer.cs index 522e07151..7e872bed1 100644 --- a/src/ImageProcessorCore/Quantizers/Octree/OctreeQuantizer.cs +++ b/src/ImageProcessorCore/Quantizers/Octree/OctreeQuantizer.cs @@ -74,9 +74,7 @@ namespace ImageProcessorCore.Quantizers /// /// Override this to process the pixel in the second pass of the algorithm /// - /// - /// The pixel to quantize - /// + /// The pixel to quantize /// /// The quantized value /// @@ -193,7 +191,7 @@ namespace ImageProcessorCore.Quantizers /// Add a given color value to the Octree /// /// - /// The containing color information to add. + /// The containing color information to add. /// public void AddColor(TColor pixel) { @@ -224,11 +222,9 @@ namespace ImageProcessorCore.Quantizers /// /// Convert the nodes in the Octree to a palette with a maximum of colorCount colors /// - /// - /// The maximum number of colors - /// + /// The maximum number of colors /// - /// An with the palletized colors + /// An with the palletized colors /// public List Palletize(int colorCount) { @@ -249,9 +245,7 @@ namespace ImageProcessorCore.Quantizers /// /// Get the palette index for the passed color /// - /// - /// The containing the pixel data. - /// + /// The containing the pixel data. /// /// The index of the given structure. /// @@ -449,12 +443,8 @@ namespace ImageProcessorCore.Quantizers /// /// Traverse the tree, building up the color palette /// - /// - /// The palette - /// - /// - /// The current palette index - /// + /// The palette + /// The current palette index public void ConstructPalette(List palette, ref int index) { if (this.leaf) @@ -487,12 +477,8 @@ namespace ImageProcessorCore.Quantizers /// /// Return the palette index for the passed color /// - /// - /// The representing the pixel. - /// - /// - /// The level. - /// + /// The representing the pixel. + /// The level. /// /// The representing the index of the pixel in the palette. /// diff --git a/src/ImageProcessorCore/Quantizers/Octree/Quantizer.cs b/src/ImageProcessorCore/Quantizers/Octree/Quantizer.cs index 7bda2d4ab..a5d933d4e 100644 --- a/src/ImageProcessorCore/Quantizers/Octree/Quantizer.cs +++ b/src/ImageProcessorCore/Quantizers/Octree/Quantizer.cs @@ -11,6 +11,8 @@ namespace ImageProcessorCore.Quantizers /// /// Encapsulates methods to calculate the color palette of an image. /// + /// The pixel format. + /// The packed format. uint, long, float. public abstract class Quantizer : IQuantizer where TColor : IPackedVector where TPacked : struct @@ -98,10 +100,10 @@ namespace ImageProcessorCore.Quantizers /// Execute a second pass through the bitmap /// /// The source image. - /// The outpuTColor pixel array + /// The output pixel array /// The width in pixels of the image /// The height in pixels of the image - protected virtual void SecondPass(PixelAccessor source, byte[] output, int width, int height) + protected virtual void SecondPass(PixelAccessor source, byte[] output, int width, int height) { Parallel.For( 0, diff --git a/src/ImageProcessorCore/Quantizers/Quantize.cs b/src/ImageProcessorCore/Quantizers/Quantize.cs index ea0102d59..c6b72430c 100644 --- a/src/ImageProcessorCore/Quantizers/Quantize.cs +++ b/src/ImageProcessorCore/Quantizers/Quantize.cs @@ -3,10 +3,10 @@ // Licensed under the Apache License, Version 2.0. // -using ImageProcessorCore.Quantizers; - namespace ImageProcessorCore { + using ImageProcessorCore.Quantizers; + /// /// Extension methods for the type. /// diff --git a/src/ImageProcessorCore/Quantizers/QuantizedImage.cs b/src/ImageProcessorCore/Quantizers/QuantizedImage.cs index f95483f37..84ebcadfe 100644 --- a/src/ImageProcessorCore/Quantizers/QuantizedImage.cs +++ b/src/ImageProcessorCore/Quantizers/QuantizedImage.cs @@ -14,8 +14,8 @@ namespace ImageProcessorCore.Quantizers /// The pixel format. /// The packed format. uint, long, float. public class QuantizedImage - where TColor : IPackedVector - where TPacked : struct + where TColor : IPackedVector + where TPacked : struct { /// /// Initializes a new instance of the class. diff --git a/src/ImageProcessorCore/Quantizers/Wu/WuQuantizer.cs b/src/ImageProcessorCore/Quantizers/Wu/WuQuantizer.cs index a1f6b8f3c..5a9aefcb2 100644 --- a/src/ImageProcessorCore/Quantizers/Wu/WuQuantizer.cs +++ b/src/ImageProcessorCore/Quantizers/Wu/WuQuantizer.cs @@ -102,7 +102,7 @@ namespace ImageProcessorCore.Quantizers private readonly byte[] tag; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public WuQuantizer() { diff --git a/src/ImageProcessorCore/Samplers/AutoOrient.cs b/src/ImageProcessorCore/Samplers/AutoOrient.cs index 33f7902c2..a0dac5d67 100644 --- a/src/ImageProcessorCore/Samplers/AutoOrient.cs +++ b/src/ImageProcessorCore/Samplers/AutoOrient.cs @@ -17,7 +17,7 @@ namespace ImageProcessorCore /// /// The pixel format. /// The packed format. uint, long, float. - /// The image to crop. + /// The image to auto rotate. /// The public static Image AutoOrient(this Image source, ProgressEventHandler progressHandler = null) where TColor : IPackedVector @@ -27,11 +27,6 @@ namespace ImageProcessorCore switch (orientation) { - case Orientation.Unknown: - case Orientation.TopLeft: - default: - return source; - case Orientation.TopRight: return source.Flip(FlipType.Horizontal, progressHandler); @@ -42,23 +37,33 @@ namespace ImageProcessorCore return source.Flip(FlipType.Vertical, progressHandler); case Orientation.LeftTop: - return source - .Rotate(RotateType.Rotate90, progressHandler) - .Flip(FlipType.Horizontal, progressHandler); + return source.Rotate(RotateType.Rotate90, progressHandler) + .Flip(FlipType.Horizontal, progressHandler); case Orientation.RightTop: return source.Rotate(RotateType.Rotate90, progressHandler); case Orientation.RightBottom: - return source - .Flip(FlipType.Vertical, progressHandler) - .Rotate(RotateType.Rotate270, progressHandler); + return source.Flip(FlipType.Vertical, progressHandler) + .Rotate(RotateType.Rotate270, progressHandler); case Orientation.LeftBottom: return source.Rotate(RotateType.Rotate270, progressHandler); + + case Orientation.Unknown: + case Orientation.TopLeft: + default: + return source; } } + /// + /// Returns the current EXIF orientation + /// + /// The pixel format. + /// The packed format. uint, long, float. + /// The image to auto rotate. + /// The private static Orientation GetExifOrientation(Image source) where TColor : IPackedVector where TPacked : struct @@ -81,4 +86,4 @@ namespace ImageProcessorCore return orientation; } } -} +} \ No newline at end of file diff --git a/src/ImageProcessorCore/Samplers/Options/ResizeHelper.cs b/src/ImageProcessorCore/Samplers/Options/ResizeHelper.cs index eab845cff..82a98c3c3 100644 --- a/src/ImageProcessorCore/Samplers/Options/ResizeHelper.cs +++ b/src/ImageProcessorCore/Samplers/Options/ResizeHelper.cs @@ -17,7 +17,8 @@ namespace ImageProcessorCore /// /// Calculates the target location and bounds to perform the resize operation against. /// - /// The type of pixels contained within the image. + /// The pixel format. + /// The packed format. uint, long, float. /// The source image. /// The resize options. /// @@ -49,7 +50,8 @@ namespace ImageProcessorCore /// /// Calculates the target rectangle for crop mode. /// - /// The type of pixels contained within the image. + /// The pixel format. + /// The packed format. uint, long, float. /// The source image. /// The resize options. /// @@ -169,7 +171,8 @@ namespace ImageProcessorCore /// /// Calculates the target rectangle for pad mode. /// - /// The type of pixels contained within the image. + /// The pixel format. + /// The packed format. uint, long, float. /// The source image. /// The resize options. /// @@ -251,7 +254,8 @@ namespace ImageProcessorCore /// /// Calculates the target rectangle for box pad mode. /// - /// The type of pixels contained within the image. + /// The pixel format. + /// The packed format. uint, long, float. /// The source image. /// The resize options. /// @@ -339,7 +343,8 @@ namespace ImageProcessorCore /// /// Calculates the target rectangle for max mode. /// - /// The type of pixels contained within the image. + /// The pixel format. + /// The packed format. uint, long, float. /// The source image. /// The resize options. /// @@ -381,7 +386,8 @@ namespace ImageProcessorCore /// /// Calculates the target rectangle for min mode. /// - /// The type of pixels contained within the image. + /// The pixel format. + /// The packed format. uint, long, float. /// The source image. /// The resize options. /// diff --git a/src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs index f20b7bce1..1f858631d 100644 --- a/src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/EntropyCropProcessor.cs @@ -27,7 +27,7 @@ namespace ImageProcessorCore.Processors /// Initializes a new instance of the class. /// /// The threshold to split the image. Must be between 0 and 1. - /// + /// /// is less than 0 or is greater than 1. /// public EntropyCropProcessor(float threshold) @@ -55,7 +55,7 @@ namespace ImageProcessorCore.Processors // Search for the first white pixels 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]); this.cropRectangle = rectangle; } diff --git a/src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs b/src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs index c1d4d5c70..1256897db 100644 --- a/src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs +++ b/src/ImageProcessorCore/Samplers/Processors/ImageSampler.cs @@ -16,4 +16,4 @@ namespace ImageProcessorCore.Processors where TPacked : struct { } -} +} \ No newline at end of file diff --git a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs index df3eea635..be005f252 100644 --- a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs @@ -2,6 +2,9 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // + +using System; + namespace ImageProcessorCore.Processors { using System.Numerics; @@ -34,7 +37,9 @@ namespace ImageProcessorCore.Processors /// protected override void OnApply(ImageBase target, ImageBase 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; } @@ -89,25 +94,26 @@ namespace ImageProcessorCore.Processors /// private bool OptimizedApply(ImageBase target, ImageBase source) { - if (Angle == 0) + const float Epsilon = .0001F; + if (Math.Abs(Angle) < Epsilon) { target.ClonePixels(target.Width, target.Height, source.Pixels); return true; } - if (Angle == 90) + if (Math.Abs(Angle - 90) < Epsilon) { this.Rotate90(target, source); return true; } - if (Angle == 180) + if (Math.Abs(Angle - 180) < Epsilon) { this.Rotate180(target, source); return true; } - if (Angle == 270) + if (Math.Abs(Angle - 270) < Epsilon) { this.Rotate270(target, source); return true; diff --git a/src/ImageProcessorCore/Samplers/Resamplers/NearestNeighborResampler.cs b/src/ImageProcessorCore/Samplers/Resamplers/NearestNeighborResampler.cs index bcd9a58b1..58b6a9d58 100644 --- a/src/ImageProcessorCore/Samplers/Resamplers/NearestNeighborResampler.cs +++ b/src/ImageProcessorCore/Samplers/Resamplers/NearestNeighborResampler.cs @@ -7,7 +7,7 @@ namespace ImageProcessorCore { /// /// 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. /// public class NearestNeighborResampler : IResampler { diff --git a/src/ImageProcessorCore/Samplers/RotateFlip.cs b/src/ImageProcessorCore/Samplers/RotateFlip.cs index 0cd406038..8bba64ca5 100644 --- a/src/ImageProcessorCore/Samplers/RotateFlip.cs +++ b/src/ImageProcessorCore/Samplers/RotateFlip.cs @@ -25,9 +25,7 @@ namespace ImageProcessorCore where TColor : IPackedVector where TPacked : struct { - return source - .Rotate(rotateType, progressHandler) - .Flip(flipType, progressHandler); + return source.Rotate(rotateType, progressHandler).Flip(flipType, progressHandler); } } } From 59315259a1db1e44395392ba421490237221bbd6 Mon Sep 17 00:00:00 2001 From: dirk Date: Fri, 26 Aug 2016 10:30:57 +0200 Subject: [PATCH 4/7] Fixed check for message in Guard.IsTrue and Guard.IsFalse Former-commit-id: ab939ab1a09d5e7452fba067ad3247bb6c56c2d3 Former-commit-id: 2a1278a2f53cc94353fe7260f017a2d0ca4caace Former-commit-id: e7a7466c02934551c49820fcb04b8b781af2c4fc --- src/ImageProcessorCore/Common/Helpers/Guard.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ImageProcessorCore/Common/Helpers/Guard.cs b/src/ImageProcessorCore/Common/Helpers/Guard.cs index 4e8c764f7..4807d17af 100644 --- a/src/ImageProcessorCore/Common/Helpers/Guard.cs +++ b/src/ImageProcessorCore/Common/Helpers/Guard.cs @@ -148,7 +148,7 @@ namespace ImageProcessorCore /// The maximum value. /// The name of the parameter that is to be checked. /// The type of the value. - /// + /// /// is less than the minimum value of greater than the maximum value. /// public static void MustBeBetweenOrEqualTo(TValue value, TValue min, TValue max, string parameterName) @@ -173,14 +173,14 @@ namespace ImageProcessorCore /// /// The error message, if any to add to the exception. /// - /// + /// /// is null /// public static void IsTrue(bool target, string parameterName, string message = "") { if (!target) { - if (string.IsNullOrWhiteSpace(message)) + if (!string.IsNullOrWhiteSpace(message)) { throw new ArgumentException(parameterName, message); } @@ -203,7 +203,7 @@ namespace ImageProcessorCore { if (target) { - if (string.IsNullOrWhiteSpace(message)) + if (!string.IsNullOrWhiteSpace(message)) { throw new ArgumentException(parameterName, message); } From 6849173a6f6854132c8247734ff4c5c64abb930e Mon Sep 17 00:00:00 2001 From: dirk Date: Fri, 26 Aug 2016 10:52:49 +0200 Subject: [PATCH 5/7] Refactored the Rational type. Former-commit-id: 1272c4c34838ae467fbe63050adb9b6a476fbcc2 Former-commit-id: a0510c5bea4432f5758af7c4058fa359794dc70b Former-commit-id: 2f5006c4a70827e9f796698303cda409a2266ef2 --- .../Numerics/BigRational.cs | 219 +++++++ src/ImageProcessorCore/Numerics/Rational.cs | 545 ++++-------------- .../Numerics/SignedRational.cs | 199 +++++++ .../Profiles/Exif/ExifReader.cs | 10 +- .../Profiles/Exif/ExifValue.cs | 10 +- .../Profiles/Exif/ExifWriter.cs | 13 +- .../Numerics/RationalTests.cs | 64 +- .../Numerics/SignedRationalTests.cs | 122 ++++ .../Profiles/Exif/ExifProfileTests.cs | 110 +--- 9 files changed, 735 insertions(+), 557 deletions(-) create mode 100644 src/ImageProcessorCore/Numerics/BigRational.cs create mode 100644 src/ImageProcessorCore/Numerics/SignedRational.cs create mode 100644 tests/ImageProcessorCore.Tests/Numerics/SignedRationalTests.cs diff --git a/src/ImageProcessorCore/Numerics/BigRational.cs b/src/ImageProcessorCore/Numerics/BigRational.cs new file mode 100644 index 000000000..965979674 --- /dev/null +++ b/src/ImageProcessorCore/Numerics/BigRational.cs @@ -0,0 +1,219 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore +{ + using System; + using System.Text; + + internal struct BigRational : IEquatable + { + 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(); + } + } +} \ No newline at end of file diff --git a/src/ImageProcessorCore/Numerics/Rational.cs b/src/ImageProcessorCore/Numerics/Rational.cs index 090af6e3c..1de71fa86 100644 --- a/src/ImageProcessorCore/Numerics/Rational.cs +++ b/src/ImageProcessorCore/Numerics/Rational.cs @@ -7,11 +7,9 @@ namespace ImageProcessorCore { using System; using System.Globalization; - using System.Numerics; - using System.Text; /// - /// Represents a number that can be expressed as a fraction + /// Represents a number that can be expressed as a fraction. /// /// /// 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 { /// - /// Represents a rational object that is not a number; NaN (0, 0) + /// Initializes a new instance of the struct. /// - public static Rational Indeterminate = new Rational(BigInteger.Zero); - - /// - /// Represents a rational object that is equal to 0 (0, 1) - /// - public static Rational Zero = new Rational(BigInteger.Zero); - - /// - /// Represents a rational object that is equal to 1 (1, 1) - /// - public static Rational One = new Rational(BigInteger.One); - - /// - /// Represents a Rational object that is equal to negative infinity (-1, 0) - /// - public static readonly Rational NegativeInfinity = new Rational(BigInteger.MinusOne, BigInteger.Zero); - - /// - /// Represents a Rational object that is equal to positive infinity (1, 0) - /// - public static readonly Rational PositiveInfinity = new Rational(BigInteger.One, BigInteger.Zero); - - /// - /// The maximum number of decimal places - /// - private const int DoubleMaxScale = 308; - - /// - /// The maximum precision (numbers after the decimal point) - /// - private static readonly BigInteger DoublePrecision = BigInteger.Pow(10, DoubleMaxScale); - - /// - /// Represents double.MaxValue - /// - private static readonly BigInteger DoubleMaxValue = (BigInteger)double.MaxValue; - - /// - /// Represents double.MinValue - /// - private static readonly BigInteger DoubleMinValue = (BigInteger)double.MinValue; - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The number above the line in a vulgar fraction showing how many of the parts - /// indicated by the denominator are taken. - /// - /// - /// The number below the line in a vulgar fraction; a divisor. - /// - public Rational(uint numerator, uint denominator) - : this() - { - this.Numerator = numerator; - this.Denominator = denominator; - - this.Simplify(); - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The number above the line in a vulgar fraction showing how many of the parts - /// indicated by the denominator are taken. - /// - /// - /// The number below the line in a vulgar fraction; a divisor. - /// - public Rational(int numerator, int denominator) - : this() + ///The to convert to an instance of this type. + public Rational(double value) + : this(value, false) { - this.Numerator = numerator; - this.Denominator = denominator; - - this.Simplify(); } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// - /// - /// The number above the line in a vulgar fraction showing how many of the parts - /// indicated by the denominator are taken. - /// - /// - /// The number below the line in a vulgar fraction; a divisor. - /// - public Rational(BigInteger numerator, BigInteger denominator) - : this() + ///The to convert to an instance of this type. + ///Specifies if the instance should be created with the best precision possible. + public Rational(double value, bool bestPrecision) { - this.Numerator = numerator; - this.Denominator = denominator; + BigRational rational = new BigRational(Math.Abs(value), bestPrecision); - this.Simplify(); + Numerator = (uint)rational.Numerator; + Denominator = (uint)rational.Denominator; } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// - /// The big integer to create the rational from. - public Rational(BigInteger value) - : this() + /// The integer to create the rational from. + public Rational(uint value) + : this(value, 1) { - this.Numerator = value; - this.Denominator = BigInteger.One; - - this.Simplify(); } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// - /// The double to create the rational from. - public Rational(double value) - : this() - { - 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)); - } - - /// - /// Gets the numerator of a number. - /// - public BigInteger Numerator { get; private set; } - - /// - /// Gets the denominator of a number. - /// - public BigInteger Denominator { get; private set; } - - /// - /// Gets a value indicating whether this instance is indeterminate. - /// - public bool IsIndeterminate + /// The number above the line in a vulgar fraction showing how many of the parts indicated by the denominator are taken. + /// The number below the line in a vulgar fraction; a divisor. + public Rational(uint numerator, uint denominator) + : this(numerator, denominator, true) { - get - { - if (this.Denominator != BigInteger.Zero) - { - return false; - } - - return this.Numerator == BigInteger.Zero; - } } /// - /// Gets a value indicating whether this instance is an integer (n, 1) + /// Initializes a new instance of the struct. /// - public bool IsInteger => this.Denominator == BigInteger.One; - - /// - /// Gets a value indicating whether this instance is equal to 0 (0, 1) - /// - public bool IsZero + /// The number above the line in a vulgar fraction showing how many of the parts indicated by the denominator are taken. + /// The number below the line in a vulgar fraction; a divisor. + /// Specified if the rational should be simplified. + public Rational(uint numerator, uint denominator, bool simplify) { - get - { - if (this.Denominator != BigInteger.One) - { - return false; - } + BigRational rational = new BigRational(numerator, denominator, simplify); - return this.Numerator == BigInteger.Zero; - } + Numerator = (uint)rational.Numerator; + Denominator = (uint)rational.Denominator; } /// - /// Gets a value indicating whether this instance is equal to 1 (1, 1) + /// Determines whether the specified instances are considered equal. /// - public bool IsOne + /// The first to compare. + /// The second to compare. + /// + public static bool operator ==(Rational left, Rational right) { - get - { - if (this.Denominator != BigInteger.One) - { - return false; - } - - return this.Numerator == BigInteger.One; - } + return Equals(left, right); } /// - /// Gets a value indicating whether this instance is equal to negative infinity (-1, 0) + /// Determines whether the specified instances are not considered equal. /// - public bool IsNegativeInfinity + /// The first to compare. + /// The second to compare. + /// + public static bool operator !=(Rational left, Rational right) { - get - { - if (this.Denominator != BigInteger.Zero) - { - return false; - } - - return this.Numerator == BigInteger.MinusOne; - } + return !Equals(left, right); } /// - /// Gets a value indicating whether this instance is equal to positive infinity (1, 0) + /// Gets the numerator of a number. /// - public bool IsPositiveInfinity + public uint Numerator { - get - { - if (this.Denominator != BigInteger.Zero) - { - return false; - } - - return this.Numerator == BigInteger.One; - } + get; + private set; } /// - /// Converts a rational number to the nearest double. + /// Gets the denominator of a number. /// - /// - /// The . - /// - public double ToDouble() + public uint Denominator { - // Shortcut return values - if (this.IsIndeterminate) - { - 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; - } + get; + private set; } - /// + /// + /// Determines whether the specified is equal to this . + /// + ///The to compare this with. public override bool Equals(object obj) { if (obj is Rational) - { - return this.Equals((Rational)obj); - } + return Equals((Rational)obj); return false; } - /// + /// + /// Determines whether the specified is equal to this . + /// + ///The to compare this with. public bool Equals(Rational other) { - // Standard: a/b = c/d - if (this.Denominator == 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; - } + BigRational left = new BigRational(Numerator, Denominator); + BigRational right = new BigRational(other.Numerator, other.Denominator); - // ad = bc - return (this.Numerator * other.Denominator) == (this.Denominator * other.Numerator); - } - - /// - public override int GetHashCode() - { - return this.GetHashCode(this); + return left.Equals(right); } - /// - public override string ToString() + /// + /// Converts the specified to an instance of this type. + /// + ///The to convert to an instance of this type. + public static Rational FromDouble(double value) { - return this.ToString(CultureInfo.InvariantCulture); + return new Rational(value, false); } - /// - /// Converts the numeric value of this instance to its equivalent string representation using - /// the specified culture-specific format information. - /// - /// - /// An object that supplies culture-specific formatting information. - /// - /// - public string ToString(IFormatProvider provider) + /// + /// Converts the specified to an instance of this type. + /// + ///The to convert to an instance of this type. + ///Specifies if the instance should be created with the best precision possible. + public static Rational FromDouble(double value, bool bestPrecision) { - if (this.IsIndeterminate) - { - 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(); + return new Rational(value, bestPrecision); } - /// - /// Simplifies the rational. - /// - private void Simplify() + /// + /// Serves as a hash of this type. + /// + public override int GetHashCode() { - if (this.IsIndeterminate) - { - 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; - } + BigRational self = new BigRational(Numerator, Denominator); + return self.GetHashCode(); } /// - /// Converts the string representation of a number into its rational value + /// Converts a rational number to the nearest . /// - /// A string that contains a number to convert. - /// The - internal static Rational Parse(string value) + /// + /// The . + /// + public double ToDouble() { - int periodIndex = value.IndexOf(".", StringComparison.Ordinal); - 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); + return Numerator / (double)Denominator; } /// - /// Returns a value indicating whether the given big integer can be - /// safely cast to a double. + /// Converts the numeric value of this instance to its equivalent string representation. /// - /// The value to test. - /// true if the value can be safely cast - private static bool SafeCastToDouble(BigInteger value) + public override string ToString() { - return DoubleMinValue <= value && value <= DoubleMaxValue; + return ToString(CultureInfo.InvariantCulture); } /// - /// 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. /// - /// - /// The instance of to return the hash code for. + /// + /// An object that supplies culture-specific formatting information. /// - /// - /// A 32-bit signed integer that is the hash code for this instance. - /// - private int GetHashCode(Rational rational) + /// + public string ToString(IFormatProvider provider) { - return ((rational.Numerator * 397) ^ rational.Denominator).GetHashCode(); + BigRational rational = new BigRational(Numerator, Denominator); + return rational.ToString(provider); } } } \ No newline at end of file diff --git a/src/ImageProcessorCore/Numerics/SignedRational.cs b/src/ImageProcessorCore/Numerics/SignedRational.cs new file mode 100644 index 000000000..d216b30d4 --- /dev/null +++ b/src/ImageProcessorCore/Numerics/SignedRational.cs @@ -0,0 +1,199 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore +{ + using System; + using System.Globalization; + + /// + /// Represents a number that can be expressed as a fraction. + /// + /// + /// This is a very simplified implimentation of a rational number designed for use with metadata only. + /// + public struct SignedRational : IEquatable + { + /// + /// Initializes a new instance of the struct. + /// + ///The to convert to an instance of this type. + public SignedRational(double value) + : this(value, false) + { + } + + /// + /// Initializes a new instance of the struct. + /// + ///The to convert to an instance of this type. + ///Specifies if the instance should be created with the best precision possible. + public SignedRational(double value, bool bestPrecision) + { + BigRational rational = new BigRational(value, bestPrecision); + + Numerator = (int)rational.Numerator; + Denominator = (int)rational.Denominator; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The integer to create the rational from. + public SignedRational(int value) + : this(value, 1) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The number above the line in a vulgar fraction showing how many of the parts indicated by the denominator are taken. + /// The number below the line in a vulgar fraction; a divisor. + public SignedRational(int numerator, int denominator) + : this(numerator, denominator, true) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The number above the line in a vulgar fraction showing how many of the parts indicated by the denominator are taken. + /// The number below the line in a vulgar fraction; a divisor. + /// Specified if the rational should be simplified. + public SignedRational(int numerator, int denominator, bool simplify) + { + BigRational rational = new BigRational(numerator, denominator, simplify); + + Numerator = (int)rational.Numerator; + Denominator = (int)rational.Denominator; + } + + /// + /// Determines whether the specified instances are considered equal. + /// + /// The first to compare. + /// The second to compare. + /// + public static bool operator ==(SignedRational left, SignedRational right) + { + return Equals(left, right); + } + + /// + /// Determines whether the specified instances are not considered equal. + /// + /// The first to compare. + /// The second to compare. + /// + public static bool operator !=(SignedRational left, SignedRational right) + { + return !Equals(left, right); + } + + /// + /// Gets the numerator of a number. + /// + public int Numerator + { + get; + private set; + } + + /// + /// Gets the denominator of a number. + /// + public int Denominator + { + get; + private set; + } + + /// + /// Determines whether the specified is equal to this . + /// + ///The to compare this with. + public override bool Equals(object obj) + { + if (obj is SignedRational) + return Equals((SignedRational)obj); + + return false; + } + + /// + /// Determines whether the specified is equal to this . + /// + ///The to compare this with. + public bool Equals(SignedRational other) + { + BigRational left = new BigRational(Numerator, Denominator); + BigRational right = new BigRational(other.Numerator, other.Denominator); + + return left.Equals(right); + } + + /// + /// Converts the specified to an instance of this type. + /// + ///The to convert to an instance of this type. + public static SignedRational FromDouble(double value) + { + return new SignedRational(value, false); + } + + /// + /// Converts the specified to an instance of this type. + /// + ///The to convert to an instance of this type. + ///Specifies if the instance should be created with the best precision possible. + public static SignedRational FromDouble(double value, bool bestPrecision) + { + return new SignedRational(value, bestPrecision); + } + + /// + /// Serves as a hash of this type. + /// + public override int GetHashCode() + { + BigRational self = new BigRational(Numerator, Denominator); + return self.GetHashCode(); + } + + /// + /// Converts a rational number to the nearest . + /// + /// + /// The . + /// + public double ToDouble() + { + return Numerator / (double)Denominator; + } + + /// + /// Converts the numeric value of this instance to its equivalent string representation. + /// + public override string ToString() + { + return ToString(CultureInfo.InvariantCulture); + } + + /// + /// Converts the numeric value of this instance to its equivalent string representation using + /// the specified culture-specific format information. + /// + /// + /// An object that supplies culture-specific formatting information. + /// + /// + public string ToString(IFormatProvider provider) + { + BigRational rational = new BigRational(Numerator, Denominator); + return rational.ToString(provider); + } + } +} \ No newline at end of file diff --git a/src/ImageProcessorCore/Profiles/Exif/ExifReader.cs b/src/ImageProcessorCore/Profiles/Exif/ExifReader.cs index 333a00d0a..546d5b846 100644 --- a/src/ImageProcessorCore/Profiles/Exif/ExifReader.cs +++ b/src/ImageProcessorCore/Profiles/Exif/ExifReader.cs @@ -442,13 +442,13 @@ namespace ImageProcessorCore { if (!this.ValidateArray(data, 8, 4)) { - return Rational.Zero; + return new Rational(); } uint numerator = BitConverter.ToUInt32(data, 0); uint denominator = BitConverter.ToUInt32(data, 4); - return new Rational(numerator, denominator); + return new Rational(numerator, denominator, false); } private sbyte ToSignedByte(byte[] data) @@ -466,17 +466,17 @@ namespace ImageProcessorCore return BitConverter.ToInt32(data, 0); } - private Rational ToSignedRational(byte[] data) + private SignedRational ToSignedRational(byte[] data) { if (!this.ValidateArray(data, 8, 4)) { - return Rational.Zero; + return new SignedRational(); } int numerator = BitConverter.ToInt32(data, 0); int denominator = BitConverter.ToInt32(data, 4); - return new Rational(numerator, denominator); + return new SignedRational(numerator, denominator, false); } private short ToSignedShort(byte[] data) diff --git a/src/ImageProcessorCore/Profiles/Exif/ExifValue.cs b/src/ImageProcessorCore/Profiles/Exif/ExifValue.cs index 3fd195b4e..a79bc85ef 100644 --- a/src/ImageProcessorCore/Profiles/Exif/ExifValue.cs +++ b/src/ImageProcessorCore/Profiles/Exif/ExifValue.cs @@ -539,13 +539,12 @@ namespace ImageProcessorCore case ExifDataType.DoubleFloat: Guard.IsTrue(type == typeof(double), nameof(value), $"Value should be a double{(this.IsArray ? " array." : ".")}"); 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: Guard.IsTrue(type == typeof(uint), nameof(value), $"Value should be an unsigned int{(this.IsArray ? " array." : ".")}"); break; + case ExifDataType.Rational: + Guard.IsTrue(type == typeof(Rational), nameof(value), $"Value should be a Rational{(this.IsArray ? " array." : ".")}"); + break; case ExifDataType.Short: Guard.IsTrue(type == typeof(ushort), nameof(value), $"Value should be an unsigned short{(this.IsArray ? " array." : ".")}"); break; @@ -555,6 +554,9 @@ namespace ImageProcessorCore case ExifDataType.SignedLong: Guard.IsTrue(type == typeof(int), nameof(value), $"Value should be an int{(this.IsArray ? " array." : ".")}"); break; + case ExifDataType.SignedRational: + Guard.IsTrue(type == typeof(SignedRational), nameof(value), $"Value should be a SignedRational{(this.IsArray ? " array." : ".")}"); + break; case ExifDataType.SignedShort: Guard.IsTrue(type == typeof(short), nameof(value), $"Value should be a short{(this.IsArray ? " array." : ".")}"); break; diff --git a/src/ImageProcessorCore/Profiles/Exif/ExifWriter.cs b/src/ImageProcessorCore/Profiles/Exif/ExifWriter.cs index 269ce52c4..e89b0d1a9 100644 --- a/src/ImageProcessorCore/Profiles/Exif/ExifWriter.cs +++ b/src/ImageProcessorCore/Profiles/Exif/ExifWriter.cs @@ -329,17 +329,16 @@ namespace ImageProcessorCore private int WriteRational(Rational value, byte[] destination, int offset) { - // Ensure no overflow - Write(BitConverter.GetBytes((uint)(value.Numerator * (value.ToDouble() < 0.0 ? -1 : 1))), destination, offset); - Write(BitConverter.GetBytes((uint)value.Denominator), destination, offset + 4); + Write(BitConverter.GetBytes(value.Numerator), destination, offset); + Write(BitConverter.GetBytes(value.Denominator), destination, offset + 4); 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((int)value.Denominator), destination, offset + 4); + Write(BitConverter.GetBytes(value.Numerator), destination, offset); + Write(BitConverter.GetBytes(value.Denominator), destination, offset + 4); return offset + 8; } @@ -370,7 +369,7 @@ namespace ImageProcessorCore case ExifDataType.SignedShort: return Write(BitConverter.GetBytes((short)value), destination, offset); case ExifDataType.SignedRational: - return this.WriteSignedRational((Rational)value, destination, offset); + return this.WriteSignedRational((SignedRational)value, destination, offset); case ExifDataType.SingleFloat: return Write(BitConverter.GetBytes((float)value), destination, offset); default: diff --git a/tests/ImageProcessorCore.Tests/Numerics/RationalTests.cs b/tests/ImageProcessorCore.Tests/Numerics/RationalTests.cs index 246349f82..e83ec5118 100644 --- a/tests/ImageProcessorCore.Tests/Numerics/RationalTests.cs +++ b/tests/ImageProcessorCore.Tests/Numerics/RationalTests.cs @@ -22,6 +22,7 @@ namespace ImageProcessorCore.Tests Rational r2 = new Rational(3, 2); Assert.Equal(r1, r2); + Assert.True(r1 == r2); Rational r3 = new Rational(7.55); Rational r4 = new Rational(755, 100); @@ -41,6 +42,7 @@ namespace ImageProcessorCore.Tests Rational second = new Rational(100, 100); Assert.NotEqual(first, second); + Assert.True(first != second); } /// @@ -49,9 +51,65 @@ namespace ImageProcessorCore.Tests [Fact] public void ConstructorAssignsProperties() { - Rational first = new Rational(4, 5); - Assert.Equal(4, first.Numerator); - Assert.Equal(5, first.Denominator); + Rational rational = new Rational(7, 55); + Assert.Equal(7U, rational.Numerator); + 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()); } } } \ No newline at end of file diff --git a/tests/ImageProcessorCore.Tests/Numerics/SignedRationalTests.cs b/tests/ImageProcessorCore.Tests/Numerics/SignedRationalTests.cs new file mode 100644 index 000000000..fff7f0fd3 --- /dev/null +++ b/tests/ImageProcessorCore.Tests/Numerics/SignedRationalTests.cs @@ -0,0 +1,122 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Tests +{ + using Xunit; + + /// + /// Tests the struct. + /// + public class SignedRationalTests + { + /// + /// Tests the equality operators for equality. + /// + [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); + } + + /// + /// Tests the equality operators for inequality. + /// + [Fact] + public void AreNotEqual() + { + SignedRational first = new SignedRational(0, 100); + SignedRational second = new SignedRational(100, 100); + + Assert.NotEqual(first, second); + Assert.True(first != second); + } + + /// + /// Tests whether the Rational constructor correctly assign properties. + /// + [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()); + } + } +} \ No newline at end of file diff --git a/tests/ImageProcessorCore.Tests/Profiles/Exif/ExifProfileTests.cs b/tests/ImageProcessorCore.Tests/Profiles/Exif/ExifProfileTests.cs index 438e90336..988c7747d 100644 --- a/tests/ImageProcessorCore.Tests/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageProcessorCore.Tests/Profiles/Exif/ExifProfileTests.cs @@ -64,12 +64,11 @@ namespace ImageProcessorCore.Tests { using (MemoryStream memStream = new MemoryStream()) { - // double exposureTime = 1.0 / 1600; - Rational exposureTime = new Rational(1, 1600); + double exposureTime = 1.0 / 1600; ExifProfile profile = GetExifProfile(); - profile.SetValue(ExifTag.ExposureTime, exposureTime); + profile.SetValue(ExifTag.ExposureTime, new Rational(exposureTime)); Image image = new Image(1, 1); image.ExifProfile = profile; @@ -84,12 +83,12 @@ namespace ImageProcessorCore.Tests ExifValue value = profile.GetValue(ExifTag.ExposureTime); Assert.NotNull(value); - Assert.Equal(exposureTime, value.Value); + Assert.NotEqual(exposureTime, ((Rational)value.Value).ToDouble()); memStream.Position = 0; profile = GetExifProfile(); - profile.SetValue(ExifTag.ExposureTime, exposureTime); + profile.SetValue(ExifTag.ExposureTime, new Rational(exposureTime, true)); image.ExifProfile = profile; image.SaveAsJpeg(memStream); @@ -101,7 +100,7 @@ namespace ImageProcessorCore.Tests Assert.NotNull(profile); 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)) { Image image = new Image(stream); - image.ExifProfile.SetValue(ExifTag.ExposureBiasValue, Rational.PositiveInfinity); + image.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.PositiveInfinity)); image = WriteAndRead(image); ExifValue value = image.ExifProfile.GetValue(ExifTag.ExposureBiasValue); 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); value = image.ExifProfile.GetValue(ExifTag.ExposureBiasValue); 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); value = image.ExifProfile.GetValue(ExifTag.FlashEnergy); 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] public void SetValue() { @@ -224,11 +148,11 @@ namespace ImageProcessorCore.Tests Assert.Throws(() => { 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); - TestValue(value, new Rational(7555, 100)); + TestValue(value, new SignedRational(7555, 100)); Assert.Throws(() => { value.Value = 75; }); @@ -258,7 +182,7 @@ namespace ImageProcessorCore.Tests TestValue(value, "ImageProcessorCore"); value = image.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); - TestValue(value, new Rational(75.55)); + TestValue(value, new SignedRational(75.55)); value = image.ExifProfile.GetValue(ExifTag.XResolution); TestValue(value, new Rational(150.0)); @@ -372,6 +296,12 @@ namespace ImageProcessorCore.Tests 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) { Assert.NotNull(value); From 7c0ae01d7b3582190b04335573866091295ab3ce Mon Sep 17 00:00:00 2001 From: dirk Date: Fri, 26 Aug 2016 12:49:28 +0200 Subject: [PATCH 6/7] Fixed check for message. Former-commit-id: 60177850e28645374a56ba7da941663e46f5e872 Former-commit-id: 50fd3960da2e9396a51dd299da7c5f287b3624a1 Former-commit-id: 0d0a319ea55a40dcc451913cff14fccfa8df5d26 --- src/ImageProcessorCore/Common/Helpers/Guard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageProcessorCore/Common/Helpers/Guard.cs b/src/ImageProcessorCore/Common/Helpers/Guard.cs index 4807d17af..c5efb01ea 100644 --- a/src/ImageProcessorCore/Common/Helpers/Guard.cs +++ b/src/ImageProcessorCore/Common/Helpers/Guard.cs @@ -26,7 +26,7 @@ namespace ImageProcessorCore { if (target == null) { - if (string.IsNullOrWhiteSpace(message)) + if (!string.IsNullOrWhiteSpace(message)) { throw new ArgumentNullException(parameterName, message); } From 4b9e294386882cbb12d2dc49c7b281658965ef32 Mon Sep 17 00:00:00 2001 From: dirk Date: Fri, 26 Aug 2016 12:58:26 +0200 Subject: [PATCH 7/7] Some more refactoring in the Guard class. Former-commit-id: a13359d3b5bb3984fdd55804226e54f0e25e9626 Former-commit-id: 2efc5aa9f7e6badd2287ae70743909023c0fb92b Former-commit-id: 049d5e9f2ff17f48f8f503cc6c34b900507a2ddc --- .../Common/Helpers/Guard.cs | 22 +++++-------------- .../Helpers/GuardTests.cs | 8 +++---- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/ImageProcessorCore/Common/Helpers/Guard.cs b/src/ImageProcessorCore/Common/Helpers/Guard.cs index c5efb01ea..1e9b3ea23 100644 --- a/src/ImageProcessorCore/Common/Helpers/Guard.cs +++ b/src/ImageProcessorCore/Common/Helpers/Guard.cs @@ -174,18 +174,13 @@ namespace ImageProcessorCore /// The error message, if any to add to the exception. /// /// - /// is null + /// is false /// - public static void IsTrue(bool target, string parameterName, string message = "") + public static void IsTrue(bool target, string parameterName, string message) { if (!target) { - if (!string.IsNullOrWhiteSpace(message)) - { - throw new ArgumentException(parameterName, message); - } - - throw new ArgumentException(parameterName); + throw new ArgumentException(message, parameterName); } } @@ -197,18 +192,13 @@ namespace ImageProcessorCore /// The name of the parameter that is to be checked. /// The error message, if any to add to the exception. /// - /// is null + /// is true /// - public static void IsFalse(bool target, string parameterName, string message = "") + public static void IsFalse(bool target, string parameterName, string message) { if (target) { - if (!string.IsNullOrWhiteSpace(message)) - { - throw new ArgumentException(parameterName, message); - } - - throw new ArgumentException(parameterName); + throw new ArgumentException(message, parameterName); } } } diff --git a/tests/ImageProcessorCore.Tests/Helpers/GuardTests.cs b/tests/ImageProcessorCore.Tests/Helpers/GuardTests.cs index 7c33470c3..b92c4e0a8 100644 --- a/tests/ImageProcessorCore.Tests/Helpers/GuardTests.cs +++ b/tests/ImageProcessorCore.Tests/Helpers/GuardTests.cs @@ -207,7 +207,7 @@ namespace ImageProcessorCore.Tests.Helpers [Fact] public void IsTrueThrowsWhenArgIsFalse() { - Assert.Throws(() => Guard.IsTrue(false, "foo")); + Assert.Throws(() => Guard.IsTrue(false, "foo", "message")); } /// @@ -216,7 +216,7 @@ namespace ImageProcessorCore.Tests.Helpers [Fact] public void IsTrueDoesThrowsWhenArgIsTrue() { - Exception ex = Record.Exception(() => Guard.IsTrue(true, "foo")); + Exception ex = Record.Exception(() => Guard.IsTrue(true, "foo", "message")); Assert.Null(ex); } @@ -226,7 +226,7 @@ namespace ImageProcessorCore.Tests.Helpers [Fact] public void IsFalseThrowsWhenArgIsFalse() { - Assert.Throws(() => Guard.IsFalse(true, "foo")); + Assert.Throws(() => Guard.IsFalse(true, "foo", "message")); } /// @@ -235,7 +235,7 @@ namespace ImageProcessorCore.Tests.Helpers [Fact] public void IsFalseDoesThrowsWhenArgIsTrue() { - Exception ex = Record.Exception(() => Guard.IsFalse(false, "foo")); + Exception ex = Record.Exception(() => Guard.IsFalse(false, "foo", "message")); Assert.Null(ex); } }