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 @@
+
+
+
+
+ Summary
+
+
+
+
+ | Assembly | .NET Core 5.0 | .NET Framework 4.6.1 | .NET Native 1.0 | ASP.NET 5 1.0 | Mono 3.3.0.0 | Windows 8.1 | Windows Phone 8.1 |
+
+ | ImageProcessor | 100% | 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.0 | ASP.NET 5 1.0 | Mono 3.3.0.0 | Windows 8.1 | Windows Phone 8.1 | Recommended 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 @@
+
+
+
+
+