diff --git a/src/ImageProcessor/ApiPortabilityAnalysis.htm b/src/ImageProcessor/ApiPortabilityAnalysis.htm new file mode 100644 index 000000000..36a7dc2de --- /dev/null +++ b/src/ImageProcessor/ApiPortabilityAnalysis.htm @@ -0,0 +1,218 @@ + + + + .NET Portability Report + + +

+ .NET Portability Report +

+

+ Summary +

+ + + + + + + + +
Assembly.NET Core 5.0.NET Framework 4.6.1.NET Native 1.0ASP.NET 5 1.0Mono 3.3.0.0Windows 8.1Windows Phone 8.1
ImageProcessor100%99.7%100%100%97.6%97.9%97.9%
+
+

+ ImageProcessor +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Target type.NET Core 5.0.NET Framework 4.6.1.NET Native 1.0ASP.NET 5 1.0Mono 3.3.0.0Windows 8.1Windows Phone 8.1Recommended changes
System.Reflection.TypeInfo
get_Assembly
         
System.Numerics.Vector4
X
Z
Y
W
#ctor(System.Single,System.Single,System.Single,System.Single)
         
System.Array
Empty``1
         
Back to summary +
\ No newline at end of file diff --git a/src/ImageProcessor/Colors/Bgra.cs b/src/ImageProcessor/Colors/Bgra.cs index 052a036e4..861e75c0b 100644 --- a/src/ImageProcessor/Colors/Bgra.cs +++ b/src/ImageProcessor/Colors/Bgra.cs @@ -12,6 +12,10 @@ namespace ImageProcessor /// /// Represents an BGRA (blue, green, red, alpha) color. /// + /// + /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, + /// as it avoids the need to create new values for modification operations. + /// [StructLayout(LayoutKind.Explicit)] public struct Bgra : IEquatable { @@ -178,6 +182,21 @@ namespace ImageProcessor [EditorBrowsable(EditorBrowsableState.Never)] public bool IsEmpty => this.B == 0 && this.G == 0 && this.R == 0 && this.A == 0; + /// + /// Allows the implicit conversion of an instance of to a + /// . + /// + /// + /// The instance of to convert. + /// + /// + /// An instance of . + /// + public static implicit operator Bgra(ColorVector color) + { + return new Bgra((255 * color.B).ToByte(), (255 * color.G).ToByte(), (255 * color.R).ToByte(), (255 * color.A).ToByte()); + } + /// /// Allows the implicit conversion of an instance of to a /// . diff --git a/src/ImageProcessor/Colors/ColorVector.cs b/src/ImageProcessor/Colors/ColorVector.cs new file mode 100644 index 000000000..6e253f2d6 --- /dev/null +++ b/src/ImageProcessor/Colors/ColorVector.cs @@ -0,0 +1,132 @@ +// +// Copyright (c) James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor +{ + using System.Numerics; + + public struct ColorVector + { + /// + /// The backing vector for SIMD support. + /// + private Vector4 backingVector; + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The blue component of this . + /// + /// + /// The green component of this . + /// + /// + /// The red component of this . + /// + /// + /// The alpha component of this . + /// + public ColorVector(double b, double g, double r, double a) + : this((float)b, (float)g, (float)r, (float)a) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The blue component of this . + /// + /// + /// The green component of this . + /// + /// + /// The red component of this . + /// + /// + /// The alpha component of this . + /// + public ColorVector(float b, float g, float r, float a) + : this() + { + this.backingVector.X = b; + this.backingVector.Y = g; + this.backingVector.Z = r; + this.backingVector.W = a; + } + + /// The color's blue component, between 0.0 and 1.0 + public float B + { + get + { + return this.backingVector.X; + } + + set + { + this.backingVector.X = value; + } + } + + /// The color's green component, between 0.0 and 1.0 + public float G + { + get + { + return this.backingVector.Y; + } + + set + { + this.backingVector.Y = value; + } + } + + /// The color's red component, between 0.0 and 1.0 + public float R + { + get + { + return this.backingVector.Z; + } + + set + { + this.backingVector.Z = value; + } + } + + /// The color's alpha component, between 0.0 and 1.0 + public float A + { + get + { + return this.backingVector.W; + } + + set + { + this.backingVector.W = value; + } + } + + /// + /// Allows the implicit conversion of an instance of to a + /// . + /// + /// + /// The instance of to convert. + /// + /// + /// An instance of . + /// + public static implicit operator ColorVector(Bgra color) + { + return new ColorVector(color.B / 255f, color.G / 255f, color.R / 255f, color.A / 255f); + } + } +} diff --git a/src/ImageProcessor/Common/Extensions/EnumerableExtensions.cs b/src/ImageProcessor/Common/Extensions/EnumerableExtensions.cs new file mode 100644 index 000000000..c492bcf8e --- /dev/null +++ b/src/ImageProcessor/Common/Extensions/EnumerableExtensions.cs @@ -0,0 +1,88 @@ +// +// Copyright (c) James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor +{ + using System; + using System.Collections.Generic; + + /// + /// Encapsulates a series of time saving extension methods to the interface. + /// + public static class EnumerableExtensions + { + /// + /// Generates a sequence of integral numbers within a specified range. + /// + /// + /// The start index, inclusive. + /// + /// + /// The end index, exclusive. + /// + /// + /// The incremental step. + /// + /// + /// The that contains a range of sequential integral numbers. + /// + public static IEnumerable SteppedRange(int fromInclusive, int toExclusive, int step) + { + // Borrowed from Enumerable.Range + long num = (fromInclusive + toExclusive) - 1L; + if ((toExclusive < 0) || (num > 0x7fffffffL)) + { + throw new ArgumentOutOfRangeException(nameof(toExclusive)); + } + + return RangeIterator(fromInclusive, i => i < toExclusive, step); + } + + /// + /// Generates a sequence of integral numbers within a specified range. + /// + /// + /// The start index, inclusive. + /// + /// + /// A method that has one parameter and returns a calculating the end index + /// + /// + /// The incremental step. + /// + /// + /// The that contains a range of sequential integral numbers. + /// + public static IEnumerable SteppedRange(int fromInclusive, Func toDelegate, int step) + { + return RangeIterator(fromInclusive, toDelegate, step); + } + + /// + /// Generates a sequence of integral numbers within a specified range. + /// + /// + /// The start index, inclusive. + /// + /// + /// A method that has one parameter and returns a calculating the end index + /// + /// + /// The incremental step. + /// + /// + /// The that contains a range of sequential integral numbers. + /// + private static IEnumerable RangeIterator(int fromInclusive, Func toDelegate, int step) + { + int i = fromInclusive; + while (toDelegate(i)) + { + yield return i; + i += step; + } + } + } +} \ No newline at end of file diff --git a/src/ImageProcessor/Filters/Alpha.cs b/src/ImageProcessor/Filters/Alpha.cs index 93c335550..2f410525a 100644 --- a/src/ImageProcessor/Filters/Alpha.cs +++ b/src/ImageProcessor/Filters/Alpha.cs @@ -6,6 +6,7 @@ namespace ImageProcessor.Filters { using System; + using System.Threading.Tasks; /// /// An to change the Alpha of an . @@ -39,18 +40,21 @@ namespace ImageProcessor.Filters int startX = sourceRectangle.X; int endX = sourceRectangle.Right; - for (int y = startY; y < endY; y++) - { - if (y >= sourceY && y < sourceBottom) - { - for (int x = startX; x < endX; x++) + Parallel.For( + startY, + endY, + y => { - Bgra color = source[x, y]; - double a = color.A * alpha; - target[x, y] = new Bgra(color.B, color.G, color.R, a.ToByte()); - } - } - } + if (y >= sourceY && y < sourceBottom) + { + for (int x = startX; x < endX; x++) + { + Bgra color = source[x, y]; + double a = color.A * alpha; + target[x, y] = new Bgra(color.B, color.G, color.R, a.ToByte()); + } + } + }); } } } diff --git a/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs b/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs index 3f7eb7591..7f42669bd 100644 --- a/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs +++ b/src/ImageProcessor/Filters/ColorMatrix/ColorMatrixFilter.cs @@ -5,6 +5,8 @@ namespace ImageProcessor.Filters { + using System.Threading.Tasks; + /// /// The color matrix filter. /// @@ -42,29 +44,32 @@ namespace ImageProcessor.Filters Bgra previousColor = source[0, 0]; Bgra pixelValue = this.ApplyMatrix(previousColor, matrix); - for (int y = startY; y < endY; y++) - { - if (y >= sourceY && y < sourceBottom) - { - for (int x = startX; x < endX; x++) + Parallel.For( + startY, + endY, + y => { - Bgra sourceColor = source[x, y]; - - // Check if this is the same as the last pixel. If so use that value - // rather than calculating it again. This is an inexpensive optimization. - if (sourceColor != previousColor) + if (y >= sourceY && y < sourceBottom) { - // Perform the operation on the pixel. - pixelValue = this.ApplyMatrix(sourceColor, matrix); + for (int x = startX; x < endX; x++) + { + Bgra sourceColor = source[x, y]; - // And setup the previous pointer - previousColor = sourceColor; - } + // Check if this is the same as the last pixel. If so use that value + // rather than calculating it again. This is an inexpensive optimization. + if (sourceColor != previousColor) + { + // Perform the operation on the pixel. + pixelValue = this.ApplyMatrix(sourceColor, matrix); - target[x, y] = pixelValue; - } - } - } + // And setup the previous pointer + previousColor = sourceColor; + } + + target[x, y] = pixelValue; + } + } + }); } /// diff --git a/src/ImageProcessor/Filters/Contrast.cs b/src/ImageProcessor/Filters/Contrast.cs index 556b3828f..c5abb79f6 100644 --- a/src/ImageProcessor/Filters/Contrast.cs +++ b/src/ImageProcessor/Filters/Contrast.cs @@ -6,6 +6,7 @@ namespace ImageProcessor.Filters { using System; + using System.Threading.Tasks; /// /// An to change the contrast of an . @@ -39,42 +40,45 @@ namespace ImageProcessor.Filters int startX = sourceRectangle.X; int endX = sourceRectangle.Right; - for (int y = startY; y < endY; y++) - { - if (y >= sourceY && y < sourceBottom) - { - for (int x = startX; x < endX; x++) + Parallel.For( + startY, + endY, + y => { - Bgra sourceColor = source[x, y]; - sourceColor = PixelOperations.ToLinear(sourceColor); + if (y >= sourceY && y < sourceBottom) + { + for (int x = startX; x < endX; x++) + { + Bgra sourceColor = source[x, y]; + sourceColor = PixelOperations.ToLinear(sourceColor); - double r = sourceColor.R / 255.0; - r -= 0.5; - r *= contrast; - r += 0.5; - r *= 255; - r = r.ToByte(); + double r = sourceColor.R / 255.0; + r -= 0.5; + r *= contrast; + r += 0.5; + r *= 255; + r = r.ToByte(); - double g = sourceColor.G / 255.0; - g -= 0.5; - g *= contrast; - g += 0.5; - g *= 255; - g = g.ToByte(); + double g = sourceColor.G / 255.0; + g -= 0.5; + g *= contrast; + g += 0.5; + g *= 255; + g = g.ToByte(); - double b = sourceColor.B / 255.0; - b -= 0.5; - b *= contrast; - b += 0.5; - b *= 255; - b = b.ToByte(); + double b = sourceColor.B / 255.0; + b -= 0.5; + b *= contrast; + b += 0.5; + b *= 255; + b = b.ToByte(); - Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), sourceColor.A); - destinationColor = PixelOperations.ToSrgb(destinationColor); - target[x, y] = destinationColor; - } - } - } + Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), sourceColor.A); + destinationColor = PixelOperations.ToSrgb(destinationColor); + target[x, y] = destinationColor; + } + } + }); } } } diff --git a/src/ImageProcessor/Filters/Invert.cs b/src/ImageProcessor/Filters/Invert.cs index d24979a2d..241835fd1 100644 --- a/src/ImageProcessor/Filters/Invert.cs +++ b/src/ImageProcessor/Filters/Invert.cs @@ -5,6 +5,8 @@ namespace ImageProcessor.Filters { + using System.Threading.Tasks; + /// /// An to invert the colors of an . /// @@ -18,19 +20,22 @@ namespace ImageProcessor.Filters int startX = sourceRectangle.X; int endX = sourceRectangle.Right; - for (int y = startY; y < endY; y++) - { - if (y >= sourceY && y < sourceBottom) - { - for (int x = startX; x < endX; x++) + Parallel.For( + startY, + endY, + y => { - // TODO: This doesn't work for gamma test images. - Bgra color = source[x, y]; - Bgra targetColor = new Bgra((255 - color.B).ToByte(), (255 - color.G).ToByte(), (255 - color.R).ToByte(), color.A); - target[x, y] = targetColor; - } - } - } + if (y >= sourceY && y < sourceBottom) + { + for (int x = startX; x < endX; x++) + { + // TODO: This doesn't work for gamma test images. + Bgra color = source[x, y]; + Bgra targetColor = new Bgra((255 - color.B).ToByte(), (255 - color.G).ToByte(), (255 - color.R).ToByte(), color.A); + target[x, y] = targetColor; + } + } + }); } } } diff --git a/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs b/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs index 1fb911bdd..ca04f2b45 100644 --- a/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs @@ -12,6 +12,7 @@ namespace ImageProcessor.Formats { using System; using System.IO; + using System.Threading.Tasks; /// /// Performs the bmp decoding operation. @@ -194,7 +195,7 @@ namespace ImageProcessor.Formats // Bit mask int mask = 0xFF >> (8 - bits); - byte[] data = new byte[(arrayWidth * height)]; + byte[] data = new byte[arrayWidth * height]; this.currentStream.Read(data, 0, data.Length); @@ -205,31 +206,34 @@ namespace ImageProcessor.Formats alignment = 4 - alignment; } - for (int y = 0; y < height; y++) - { - int rowOffset = y * (arrayWidth + alignment); + Parallel.For( + 0, + height, + y => + { + int rowOffset = y * (arrayWidth + alignment); - for (int x = 0; x < arrayWidth; x++) - { - int offset = rowOffset + x; + for (int x = 0; x < arrayWidth; x++) + { + int offset = rowOffset + x; - // Revert the y value, because bitmaps are saved from down to top - int row = Invert(y, height); + // Revert the y value, because bitmaps are saved from down to top + int row = Invert(y, height); - int colOffset = x * ppb; + int colOffset = x * ppb; - for (int shift = 0; shift < ppb && (colOffset + shift) < width; shift++) - { - int colorIndex = (data[offset] >> (8 - bits - (shift * bits))) & mask; + for (int shift = 0; shift < ppb && (colOffset + shift) < width; shift++) + { + int colorIndex = (data[offset] >> (8 - bits - (shift * bits))) & mask; - int arrayOffset = ((row * width) + (colOffset + shift)) * 4; - imageData[arrayOffset + 0] = colors[colorIndex * 4]; - imageData[arrayOffset + 1] = colors[(colorIndex * 4) + 1]; - imageData[arrayOffset + 2] = colors[(colorIndex * 4) + 2]; - imageData[arrayOffset + 3] = 255; - } - } - } + int arrayOffset = ((row * width) + (colOffset + shift)) * 4; + imageData[arrayOffset + 0] = colors[colorIndex * 4]; + imageData[arrayOffset + 1] = colors[(colorIndex * 4) + 1]; + imageData[arrayOffset + 2] = colors[(colorIndex * 4) + 2]; + imageData[arrayOffset + 3] = 255; + } + } + }); } /// diff --git a/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs b/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs index ed833bf62..bbb2d6538 100644 --- a/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs @@ -7,6 +7,7 @@ namespace ImageProcessor.Formats { using System; using System.IO; + using System.Threading.Tasks; /// /// Image encoder for writing an image to a stream as a Windows bitmap. diff --git a/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs b/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs index 766923f7e..587a1bf18 100644 --- a/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs +++ b/src/ImageProcessor/Formats/Jpg/JpegDecoder.cs @@ -7,6 +7,8 @@ namespace ImageProcessor.Formats { using System; using System.IO; + using System.Threading.Tasks; + using BitMiracle.LibJpeg; /// @@ -104,43 +106,34 @@ namespace ImageProcessor.Formats throw new NotSupportedException("JpegDecoder only support RGB color space."); } - for (int y = 0; y < pixelHeight; y++) - { - SampleRow row = jpg.GetRow(y); + Parallel.For( + 0, + pixelHeight, + y => + { + SampleRow row = jpg.GetRow(y); - for (int x = 0; x < pixelWidth; x++) - { - Sample sample = row.GetAt(x); + for (int x = 0; x < pixelWidth; x++) + { + Sample sample = row.GetAt(x); - int offset = ((y * pixelWidth) + x) * 4; + int offset = ((y * pixelWidth) + x) * 4; - pixels[offset + 0] = (byte)sample[2]; - pixels[offset + 1] = (byte)sample[1]; - pixels[offset + 2] = (byte)sample[0]; - pixels[offset + 3] = 255; - } - } + pixels[offset + 0] = (byte)sample[2]; + pixels[offset + 1] = (byte)sample[1]; + pixels[offset + 2] = (byte)sample[0]; + pixels[offset + 3] = 255; + } + }); image.SetPixels(pixelWidth, pixelHeight, pixels); } /// - /// + /// Returns a value indicating whether the given bytes identify Jpeg data. /// - /// - /// - private bool IsExif(byte[] header) - { - bool isExif = - header[6] == 0x45 && // E - header[7] == 0x78 && // x - header[8] == 0x69 && // i - header[9] == 0x66 && // f - header[10] == 0x00; - - return isExif; - } - + /// The bytes representing the file header. + /// The private static bool IsJpeg(byte[] header) { bool isJpg = @@ -152,5 +145,22 @@ namespace ImageProcessor.Formats return isJpg; } + + /// + /// Returns a value indicating whether the given bytes identify EXIF data. + /// + /// The bytes representing the file header. + /// The + private bool IsExif(byte[] header) + { + bool isExif = + header[6] == 0x45 && // E + header[7] == 0x78 && // x + header[8] == 0x69 && // i + header[9] == 0x66 && // f + header[10] == 0x00; + + return isExif; + } } } diff --git a/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs b/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs index 35d6e286e..60971697c 100644 --- a/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs +++ b/src/ImageProcessor/Formats/Jpg/JpegEncoder.cs @@ -7,6 +7,7 @@ namespace ImageProcessor.Formats { using System; using System.IO; + using System.Threading.Tasks; using BitMiracle.LibJpeg; @@ -92,22 +93,25 @@ namespace ImageProcessor.Formats SampleRow[] rows = new SampleRow[pixelHeight]; - for (int y = 0; y < pixelHeight; y++) - { - byte[] samples = new byte[pixelWidth * 3]; - - for (int x = 0; x < pixelWidth; x++) - { - int start = x * 3; - int source = ((y * pixelWidth) + x) * 4; - - samples[start] = sourcePixels[source + 2]; - samples[start + 1] = sourcePixels[source + 1]; - samples[start + 2] = sourcePixels[source]; - } - - rows[y] = new SampleRow(samples, pixelWidth, 8, 3); - } + Parallel.For( + 0, + pixelHeight, + y => + { + byte[] samples = new byte[pixelWidth * 3]; + + for (int x = 0; x < pixelWidth; x++) + { + int start = x * 3; + int source = ((y * pixelWidth) + x) * 4; + + samples[start] = sourcePixels[source + 2]; + samples[start + 1] = sourcePixels[source + 1]; + samples[start + 2] = sourcePixels[source]; + } + + rows[y] = new SampleRow(samples, pixelWidth, 8, 3); + }); JpegImage jpg = new JpegImage(rows, Colorspace.RGB); jpg.WriteJpeg(stream, new CompressionParameters { Quality = this.Quality }); diff --git a/src/ImageProcessor/Formats/Png/Zlib/InflaterInputBuffer.cs b/src/ImageProcessor/Formats/Png/Zlib/InflaterInputBuffer.cs index 42fa1753a..5c70ad41c 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/InflaterInputBuffer.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/InflaterInputBuffer.cs @@ -3,9 +3,6 @@ using System; using System.IO; - //using ICSharpCode.SharpZipLib.Zip; - //using ICSharpCode.SharpZipLib.Zip.Compression; - /// /// An input buffer customised for use by /// @@ -14,7 +11,6 @@ /// public class InflaterInputBuffer { - #region Constructors /// /// Initialise a new instance of with a default buffer size /// @@ -39,7 +35,6 @@ rawData = new byte[bufferSize]; clearText = rawData; } - #endregion /// /// Get the length of bytes bytes in the @@ -127,17 +122,7 @@ toRead -= count; } -#if !NETCF_1_0 && !NOCRYPTO - if (cryptoTransform != null) - { - clearTextLength = cryptoTransform.TransformBlock(rawData, 0, rawLength, clearText, 0); - } - else -#endif - { - clearTextLength = rawLength; - } - + clearTextLength = rawLength; available = clearTextLength; } @@ -178,12 +163,14 @@ return 0; } } + int toCopy = Math.Min(currentLength, available); - System.Array.Copy(rawData, rawLength - (int)available, outBuffer, currentOffset, toCopy); + Array.Copy(rawData, rawLength - (int)available, outBuffer, currentOffset, toCopy); currentOffset += toCopy; currentLength -= toCopy; available -= toCopy; } + return length; } @@ -270,57 +257,12 @@ return (uint)ReadLeInt() | ((long)ReadLeInt() << 32); } -#if !NETCF_1_0 && !NOCRYPTO - /// - /// Get/set the to apply to any data. - /// - /// Set this value to null to have no transform applied. - public ICryptoTransform CryptoTransform - { - set - { - cryptoTransform = value; - if (cryptoTransform != null) - { - if (rawData == clearText) - { - if (internalClearText == null) - { - internalClearText = new byte[rawData.Length]; - } - clearText = internalClearText; - } - clearTextLength = rawLength; - if (available > 0) - { - cryptoTransform.TransformBlock(rawData, rawLength - available, available, clearText, rawLength - available); - } - } - else - { - clearText = rawData; - clearTextLength = rawLength; - } - } - } -#endif - - #region Instance Fields int rawLength; byte[] rawData; int clearTextLength; byte[] clearText; -#if !NETCF_1_0 && !NOCRYPTO - byte[] internalClearText; -#endif - int available; - -#if !NETCF_1_0 && !NOCRYPTO - ICryptoTransform cryptoTransform; -#endif Stream inputStream; - #endregion } } diff --git a/src/ImageProcessor/Formats/Png/Zlib/InflaterInputStream.cs b/src/ImageProcessor/Formats/Png/Zlib/InflaterInputStream.cs index 62cba8bb4..b6a35e420 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/InflaterInputStream.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/InflaterInputStream.cs @@ -149,16 +149,6 @@ } } - /// - /// Clear any cryptographic state. - /// - protected void StopDecrypting() - { -#if !NETCF_1_0 && !NOCRYPTO - inputBuffer.CryptoTransform = null; -#endif - } - /// /// Returns 0 once the end of the stream (EOF) has been reached. /// Otherwise returns 1. diff --git a/src/ImageProcessor/Formats/Png/Zlib/ZipConstants.cs b/src/ImageProcessor/Formats/Png/Zlib/ZipConstants.cs index 6dccc6a96..9ba46abe4 100644 --- a/src/ImageProcessor/Formats/Png/Zlib/ZipConstants.cs +++ b/src/ImageProcessor/Formats/Png/Zlib/ZipConstants.cs @@ -7,7 +7,6 @@ /// public static class ZipConstants { - #region Versions /// /// The version made by field for entries in the central header when created by this library /// @@ -30,9 +29,7 @@ /// The version required for Zip64 extensions (4.5 or higher) /// public const int VersionZip64 = 45; - #endregion - #region Header Sizes /// /// Size of local entry header (excluding variable length fields at end) /// @@ -62,9 +59,7 @@ /// Size of 'classic' cryptographic header stored before any entry data /// public const int CryptoHeaderSize = 12; - #endregion - #region Header Signatures /// /// Signature for local entry header @@ -121,52 +116,8 @@ /// End of central directory record signature /// public const int EndOfCentralDirectorySignature = 'P' | ('K' << 8) | (5 << 16) | (6 << 24); - - #endregion - -#if NETCF_1_0 || NETCF_2_0 - // This isnt so great but is better than nothing. - // Trying to work out an appropriate OEM code page would be good. - // 850 is a good default for english speakers particularly in Europe. - static int defaultCodePage = CultureInfo.CurrentCulture.TextInfo.ANSICodePage; -#elif PCL static Encoding defaultEncoding = Encoding.UTF8; -#else - /// - /// Get OEM codepage from NetFX, which parses the NLP file with culture info table etc etc. - /// But sometimes it yields the special value of 1 which is nicknamed CodePageNoOEM in sources (might also mean CP_OEMCP, but Encoding puts it so). - /// This was observed on Ukranian and Hindu systems. - /// Given this value, throws an . - /// So replace it with some fallback, e.g. 437 which is the default cpcp in a console in a default Windows installation. - /// - static int defaultCodePage = - // these values cause ArgumentException in subsequent calls to Encoding::GetEncoding() - ((Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 1) || (Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 2) || (Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 3) || (Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 42)) - ? 437 // The default OEM encoding in a console in a default Windows installation, as a fallback. - : Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage; -#endif -#if !PCL - /// - /// Default encoding used for string conversion. 0 gives the default system OEM code page. - /// Dont use unicode encodings if you want to be Zip compatible! - /// Using the default code page isnt the full solution neccessarily - /// there are many variable factors, codepage 850 is often a good choice for - /// European users, however be careful about compatability. - /// - public static int DefaultCodePage { - get { - return defaultCodePage; - } - set { - if ((value < 0) || (value > 65535) || - (value == 1) || (value == 2) || (value == 3) || (value == 42)) { - throw new ArgumentOutOfRangeException("value"); - } - - defaultCodePage = value; - } - } -#else + /// /// PCL don't support CodePage so we used Encoding instead of /// @@ -176,12 +127,12 @@ { return defaultEncoding; } + set { defaultEncoding = value; } } -#endif /// /// Convert a portion of a byte array to a string. @@ -201,11 +152,8 @@ { return string.Empty; } -#if !PCL - return Encoding.GetEncoding(DefaultCodePage).GetString(data, 0, count); -#else + return DefaultEncoding.GetString(data, 0, count); -#endif } /// @@ -294,11 +242,8 @@ { return new byte[0]; } -#if !PCL - return Encoding.GetEncoding(DefaultCodePage).GetBytes(str); -#else + return DefaultEncoding.GetBytes(str); -#endif } /// @@ -320,10 +265,8 @@ { return Encoding.UTF8.GetBytes(str); } - else - { - return ConvertToArray(str); - } + + return ConvertToArray(str); } } } diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index 77ea223ad..0c53f80f1 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -42,6 +42,8 @@ + + diff --git a/src/ImageProcessor/Samplers/Resize - Copy.cs b/src/ImageProcessor/Samplers/Resize - Copy.cs new file mode 100644 index 000000000..d94b3753e --- /dev/null +++ b/src/ImageProcessor/Samplers/Resize - Copy.cs @@ -0,0 +1,232 @@ +// +// Copyright (c) James South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessor.Samplers +{ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + + /// + /// Provides methods that allow the resizing of images using various resampling algorithms. + /// + public class Resize : ParallelImageProcessor + { + /// + /// The epsilon for comparing floating point numbers. + /// + private const float Epsilon = 0.0001f; + + /// + /// The horizontal weights. + /// + private Weights[] horizontalWeights; + + /// + /// The vertical weights. + /// + private Weights[] verticalWeights; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The sampler to perform the resize operation. + /// + public Resize(IResampler sampler) + { + Guard.NotNull(sampler, nameof(sampler)); + + this.Sampler = sampler; + } + + /// + /// Gets the sampler to perform the resize operation. + /// + public IResampler Sampler { get; } + + /// + protected override void OnApply(Rectangle targetRectangle, Rectangle sourceRectangle) + { + this.horizontalWeights = this.PrecomputeWeights(targetRectangle.Width, sourceRectangle.Width); + this.verticalWeights = this.PrecomputeWeights(targetRectangle.Height, sourceRectangle.Height); + } + + /// + protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) + { + int targetY = targetRectangle.Y; + int targetBottom = targetRectangle.Bottom; + int startX = targetRectangle.X; + int endX = targetRectangle.Right; + + Parallel.For( + startY, + endY, + y => + { + if (y >= targetY && y < targetBottom) + { + List verticalValues = this.verticalWeights[y].Values; + double verticalSum = this.verticalWeights[y].Sum; + + for (int x = startX; x < endX; x++) + { + List horizontalValues = this.horizontalWeights[x].Values; + double horizontalSum = this.horizontalWeights[x].Sum; + + // Destination color components + double r = 0; + double g = 0; + double b = 0; + double a = 0; + + foreach (Weight yw in verticalValues) + { + if (Math.Abs(yw.Value) < Epsilon) + { + continue; + } + + int originY = yw.Index; + + foreach (Weight xw in horizontalValues) + { + if (Math.Abs(xw.Value) < Epsilon) + { + continue; + } + + int originX = xw.Index; + Bgra sourceColor = source[originX, originY]; + //ColorVector sourceColor = PixelOperations.ToLinear(source[originX, originY]); + + r += sourceColor.R * (yw.Value / verticalSum) * (xw.Value / horizontalSum); + g += sourceColor.G * (yw.Value / verticalSum) * (xw.Value / horizontalSum); + b += sourceColor.B * (yw.Value / verticalSum) * (xw.Value / horizontalSum); + a += sourceColor.A * (yw.Value / verticalSum) * (xw.Value / horizontalSum); + } + } + + // TODO: Double cast. + Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), a.ToByte()); + //Bgra destinationColor = PixelOperations.ToSrgb(new ColorVector(b, g, r, a)); + target[x, y] = destinationColor; + } + } + }); + } + + /// + /// Computes the weights to apply at each pixel when resizing. + /// + /// The destination section size. + /// The source section size. + /// + /// The . + /// + private Weights[] PrecomputeWeights(int destinationSize, int sourceSize) + { + IResampler sampler = this.Sampler; + double du = sourceSize / (double)destinationSize; + double scale = du; + + if (scale < 1) + { + scale = 1; + } + + double ru = Math.Ceiling(scale * sampler.Radius); + Weights[] result = new Weights[destinationSize]; + + for (int i = 0; i < destinationSize; i++) + { + double fu = ((i + .5) * du) - 0.5; + int startU = (int)Math.Ceiling(fu - ru); + + if (startU < 0) + { + startU = 0; + } + + int endU = (int)Math.Floor(fu + ru); + + if (endU > sourceSize - 1) + { + endU = sourceSize - 1; + } + + double sum = 0; + result[i] = new Weights(); + + for (int a = startU; a <= endU; a++) + { + double w = 255 * sampler.GetValue((a - fu) / scale); + + if (Math.Abs(w) > Epsilon) + { + sum += w; + result[i].Values.Add(new Weight(a, w)); + } + } + + result[i].Sum = sum; + } + + return result; + } + + /// + /// Represents the weight to be added to a scaled pixel. + /// + protected struct Weight + { + /// + /// The pixel index. + /// + public readonly int Index; + + /// + /// The result of the interpolation algorithm. + /// + public readonly double Value; + + /// + /// Initializes a new instance of the struct. + /// + /// The index. + /// The value. + public Weight(int index, double value) + { + this.Index = index; + this.Value = value; + } + } + + /// + /// Represents a collection of weights and their sum. + /// + protected class Weights + { + /// + /// Initializes a new instance of the class. + /// + public Weights() + { + this.Values = new List(); + } + + /// + /// Gets or sets the values. + /// + public List Values { get; set; } + + /// + /// Gets or sets the sum. + /// + public double Sum { get; set; } + } + } +} diff --git a/src/ImageProcessor/Samplers/Resize.cs b/src/ImageProcessor/Samplers/Resize.cs index 015f6d2d7..1010aa17c 100644 --- a/src/ImageProcessor/Samplers/Resize.cs +++ b/src/ImageProcessor/Samplers/Resize.cs @@ -7,6 +7,7 @@ namespace ImageProcessor.Samplers { using System; using System.Collections.Generic; + using System.Threading.Tasks; /// /// Provides methods that allow the resizing of images using various resampling algorithms. @@ -61,57 +62,61 @@ namespace ImageProcessor.Samplers int startX = targetRectangle.X; int endX = targetRectangle.Right; - for (int y = startY; y < endY; y++) - { - if (y >= targetY && y < targetBottom) + Parallel.For( + startY, + endY, + y => { - List verticalValues = this.verticalWeights[y].Values; - double verticalSum = this.verticalWeights[y].Sum; - - for (int x = startX; x < endX; x++) + if (y >= targetY && y < targetBottom) { - List horizontalValues = this.horizontalWeights[x].Values; - double horizontalSum = this.horizontalWeights[x].Sum; + List verticalValues = this.verticalWeights[y].Values; + double verticalSum = this.verticalWeights[y].Sum; - // Destination color components - double r = 0; - double g = 0; - double b = 0; - double a = 0; - - foreach (Weight yw in verticalValues) + for (int x = startX; x < endX; x++) { - if (Math.Abs(yw.Value) < Epsilon) - { - continue; - } + List horizontalValues = this.horizontalWeights[x].Values; + double horizontalSum = this.horizontalWeights[x].Sum; - int originY = yw.Index; + // Destination color components + double r = 0; + double g = 0; + double b = 0; + double a = 0; - foreach (Weight xw in horizontalValues) + foreach (Weight yw in verticalValues) { - if (Math.Abs(xw.Value) < Epsilon) + if (Math.Abs(yw.Value) < Epsilon) { continue; } - int originX = xw.Index; - Bgra sourceColor = source[originX, originY]; - sourceColor = PixelOperations.ToLinear(sourceColor); + int originY = yw.Index; - r += sourceColor.R * (yw.Value / verticalSum) * (xw.Value / horizontalSum); - g += sourceColor.G * (yw.Value / verticalSum) * (xw.Value / horizontalSum); - b += sourceColor.B * (yw.Value / verticalSum) * (xw.Value / horizontalSum); - a += sourceColor.A * (yw.Value / verticalSum) * (xw.Value / horizontalSum); + foreach (Weight xw in horizontalValues) + { + if (Math.Abs(xw.Value) < Epsilon) + { + continue; + } + + int originX = xw.Index; + Bgra sourceColor = source[originX, originY]; + //ColorVector sourceColor = PixelOperations.ToLinear(source[originX, originY]); + + r += sourceColor.R * (yw.Value / verticalSum) * (xw.Value / horizontalSum); + g += sourceColor.G * (yw.Value / verticalSum) * (xw.Value / horizontalSum); + b += sourceColor.B * (yw.Value / verticalSum) * (xw.Value / horizontalSum); + a += sourceColor.A * (yw.Value / verticalSum) * (xw.Value / horizontalSum); + } } - } - Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), a.ToByte()); - destinationColor = PixelOperations.ToSrgb(destinationColor); - target[x, y] = destinationColor; + // TODO: Double cast. + Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), a.ToByte()); + //Bgra destinationColor = PixelOperations.ToSrgb(new ColorVector(b, g, r, a)); + target[x, y] = destinationColor; + } } - } - } + }); } /// @@ -136,39 +141,42 @@ namespace ImageProcessor.Samplers double ru = Math.Ceiling(scale * sampler.Radius); Weights[] result = new Weights[destinationSize]; - for (int i = 0; i < destinationSize; i++) - { - double fu = ((i + .5) * du) - 0.5; - int startU = (int)Math.Ceiling(fu - ru); + Parallel.For( + 0, + destinationSize, + i => + { + double fu = ((i + .5) * du) - 0.5; + int startU = (int)Math.Ceiling(fu - ru); - if (startU < 0) - { - startU = 0; - } + if (startU < 0) + { + startU = 0; + } - int endU = (int)Math.Floor(fu + ru); + int endU = (int)Math.Floor(fu + ru); - if (endU > sourceSize - 1) - { - endU = sourceSize - 1; - } + if (endU > sourceSize - 1) + { + endU = sourceSize - 1; + } - double sum = 0; - result[i] = new Weights(); + double sum = 0; + result[i] = new Weights(); - for (int a = startU; a <= endU; a++) - { - double w = 255 * sampler.GetValue((a - fu) / scale); + for (int a = startU; a <= endU; a++) + { + double w = 255 * sampler.GetValue((a - fu) / scale); - if (Math.Abs(w) > Epsilon) - { - sum += w; - result[i].Values.Add(new Weight(a, w)); - } - } + if (Math.Abs(w) > Epsilon) + { + sum += w; + result[i].Values.Add(new Weight(a, w)); + } + } - result[i].Sum = sum; - } + result[i].Sum = sum; + }); return result; } diff --git a/tests/ImageProcessor.Tests/ImageProcessor.Tests.csproj b/tests/ImageProcessor.Tests/ImageProcessor.Tests.csproj index fd13cac0e..ff06126e0 100644 --- a/tests/ImageProcessor.Tests/ImageProcessor.Tests.csproj +++ b/tests/ImageProcessor.Tests/ImageProcessor.Tests.csproj @@ -10,13 +10,14 @@ Properties ImageProcessor.Tests ImageProcessor.Tests - v4.5 + v4.6 512 {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} ..\..\ true + true @@ -37,6 +38,11 @@ + + + ..\..\packages\System.Numerics.Vectors.4.1.0\lib\net46\System.Numerics.Vectors.dll + True + ..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll True diff --git a/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs b/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs index 34c8656cd..07d9f5e25 100644 --- a/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs +++ b/tests/ImageProcessor.Tests/Processors/ProcessorTestBase.cs @@ -19,15 +19,15 @@ namespace ImageProcessor.Tests /// public static readonly List Files = new List { - //"../../TestImages/Formats/Jpg/Backdrop.jpg", - //"../../TestImages/Formats/Jpg/Calliphora.jpg", - //"../../TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg", + "../../TestImages/Formats/Jpg/Backdrop.jpg", + "../../TestImages/Formats/Jpg/Calliphora.jpg", + "../../TestImages/Formats/Jpg/gamma_dalai_lama_gray.jpg", "../../TestImages/Formats/Jpg/greyscale.jpg", - //"../../TestImages/Formats/Bmp/Car.bmp", - //"../../TestImages/Formats/Png/cmyk.png", - //"../../TestImages/Formats/Png/gamma-1.0-or-2.2.png", - //"../../TestImages/Formats/Gif/leaf.gif", - //"../../TestImages/Formats/Gif/rings.gif" + "../../TestImages/Formats/Bmp/Car.bmp", + "../../TestImages/Formats/Png/cmyk.png", + "../../TestImages/Formats/Png/gamma-1.0-or-2.2.png", + "../../TestImages/Formats/Gif/leaf.gif", + "../../TestImages/Formats/Gif/rings.gif" // { "../../TestImages/Formats/Gif/ani.gif" }, // { "../../TestImages/Formats/Gif/ani2.gif" }, diff --git a/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs b/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs index 49ad6576b..cd42165c8 100644 --- a/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs +++ b/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs @@ -1,6 +1,7 @@  namespace ImageProcessor.Tests { + using System; using System.Diagnostics; using System.IO; diff --git a/tests/ImageProcessor.Tests/packages.config b/tests/ImageProcessor.Tests/packages.config index 3ef6ebb2a..8cba8428f 100644 --- a/tests/ImageProcessor.Tests/packages.config +++ b/tests/ImageProcessor.Tests/packages.config @@ -1,5 +1,10 @@  + + + + +