diff --git a/Settings.StyleCop b/Settings.StyleCop
index b7a5355e0..aeee88627 100644
--- a/Settings.StyleCop
+++ b/Settings.StyleCop
@@ -32,6 +32,8 @@
Vol
pp
cmyk
+ Paeth
+ th
diff --git a/src/ImageSharp/Common/Extensions/StreamExtensions.cs b/src/ImageSharp/Common/Extensions/StreamExtensions.cs
index 87d5a6c32..6de94dd22 100644
--- a/src/ImageSharp/Common/Extensions/StreamExtensions.cs
+++ b/src/ImageSharp/Common/Extensions/StreamExtensions.cs
@@ -5,10 +5,19 @@
namespace ImageSharp
{
+ using System.Buffers;
using System.IO;
+ ///
+ /// Extension methods for the type.
+ ///
internal static class StreamExtensions
{
+ ///
+ /// Skips the number of bytes in the given stream.
+ ///
+ /// The stream.
+ /// The count.
public static void Skip(this Stream stream, int count)
{
if (count < 1)
@@ -22,8 +31,15 @@ namespace ImageSharp
}
else
{
- byte[] foo = new byte[count];
- stream.Read(foo, 0, count);
+ byte[] foo = ArrayPool.Shared.Rent(count);
+ try
+ {
+ stream.Read(foo, 0, count);
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(foo);
+ }
}
}
}
diff --git a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
index 9cb4fce26..69161eb02 100644
--- a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
@@ -246,7 +246,6 @@ namespace ImageSharp.Formats
///
/// The AC luminance huffman table index
///
-
LuminanceAC = 1,
// ReSharper restore UnusedMember.Local
@@ -851,6 +850,7 @@ namespace ImageSharp.Formats
Block b = new Block();
Block cb = new Block();
Block cr = new Block();
+
// ReSharper disable once InconsistentNaming
int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
@@ -880,6 +880,7 @@ namespace ImageSharp.Formats
Block b = new Block();
Block[] cb = new Block[4];
Block[] cr = new Block[4];
+
// ReSharper disable once InconsistentNaming
int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
index bef124541..cd95eba56 100644
--- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
@@ -12,7 +12,7 @@ namespace ImageSharp.Formats
/// the value of a pixel.
///
///
- internal static class AverageFilter
+ internal static unsafe class AverageFilter
{
///
/// Decodes the scanline
@@ -28,12 +28,17 @@ namespace ImageSharp.Formats
// Average(x) + floor((Raw(x-bpp)+Prior(x))/2)
byte[] result = new byte[scanline.Length];
- for (int x = 1; x < scanline.Length; x++)
+ fixed (byte* scan = scanline)
+ fixed (byte* prev = previousScanline)
+ fixed (byte* res = result)
{
- byte left = (x - bytesPerPixel < 1) ? (byte)0 : result[x - bytesPerPixel];
- byte above = previousScanline[x];
+ for (int x = 1; x < scanline.Length; x++)
+ {
+ byte left = (x - bytesPerPixel < 1) ? (byte)0 : res[x - bytesPerPixel];
+ byte above = prev[x];
- result[x] = (byte)((scanline[x] + Average(left, above)) % 256);
+ res[x] = (byte)((scan[x] + Average(left, above)) % 256);
+ }
}
return result;
@@ -44,25 +49,29 @@ namespace ImageSharp.Formats
///
/// The scanline to encode
/// The previous scanline.
+ /// The encoded scanline.
/// The bytes per pixel.
/// The number of bytes per scanline
/// The
- public static byte[] Encode(byte[] scanline, byte[] previousScanline, int bytesPerPixel, int bytesPerScanline)
+ public static byte[] Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel, int bytesPerScanline)
{
// Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2)
- byte[] encodedScanline = new byte[bytesPerScanline + 1];
-
- encodedScanline[0] = (byte)FilterType.Average;
-
- for (int x = 0; x < bytesPerScanline; x++)
+ fixed (byte* scan = scanline)
+ fixed (byte* prev = previousScanline)
+ fixed (byte* res = result)
{
- byte left = (x - bytesPerPixel < 0) ? (byte)0 : scanline[x - bytesPerPixel];
- byte above = previousScanline[x];
+ res[0] = 3;
- encodedScanline[x + 1] = (byte)((scanline[x] - Average(left, above)) % 256);
+ for (int x = 0; x < bytesPerScanline; x++)
+ {
+ byte left = (x - bytesPerPixel < 0) ? (byte)0 : scan[x - bytesPerPixel];
+ byte above = prev[x];
+
+ res[x + 1] = (byte)((scan[x] - Average(left, above)) % 256);
+ }
}
- return encodedScanline;
+ return result;
}
///
@@ -73,7 +82,7 @@ namespace ImageSharp.Formats
/// The
private static int Average(byte left, byte above)
{
- return Convert.ToInt32(Math.Floor((left + above) / 2.0D));
+ return (left + above) >> 1;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs b/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs
index 175e5affa..ef41fc631 100644
--- a/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs
@@ -29,16 +29,13 @@ namespace ImageSharp.Formats
/// Encodes the scanline
///
/// The scanline to encode
+ /// The encoded scanline.
/// The number of bytes per scanline
- /// The
- public static byte[] Encode(byte[] scanline, int bytesPerScanline)
+ public static void Encode(byte[] scanline, byte[] result, int bytesPerScanline)
{
// Insert a byte before the data.
- byte[] encodedScanline = new byte[bytesPerScanline + 1];
- encodedScanline[0] = (byte)FilterType.None;
- Buffer.BlockCopy(scanline, 0, encodedScanline, 1, bytesPerScanline);
-
- return encodedScanline;
+ result[0] = 0;
+ Buffer.BlockCopy(scanline, 0, result, 1, bytesPerScanline);
}
}
}
diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
index 232d7cc3d..16c0378e9 100644
--- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
@@ -13,7 +13,7 @@ namespace ImageSharp.Formats
/// This technique is due to Alan W. Paeth.
///
///
- internal static class PaethFilter
+ internal static unsafe class PaethFilter
{
///
/// Decodes the scanline
@@ -27,13 +27,18 @@ namespace ImageSharp.Formats
// Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp))
byte[] result = new byte[scanline.Length];
- for (int x = 1; x < scanline.Length; x++)
+ fixed (byte* scan = scanline)
+ fixed (byte* prev = previousScanline)
+ fixed (byte* res = result)
{
- byte left = (x - bytesPerPixel < 1) ? (byte)0 : result[x - bytesPerPixel];
- byte above = previousScanline[x];
- byte upperLeft = (x - bytesPerPixel < 1) ? (byte)0 : previousScanline[x - bytesPerPixel];
+ for (int x = 1; x < scanline.Length; x++)
+ {
+ byte left = (x - bytesPerPixel < 1) ? (byte)0 : res[x - bytesPerPixel];
+ byte above = prev[x];
+ byte upperLeft = (x - bytesPerPixel < 1) ? (byte)0 : prev[x - bytesPerPixel];
- result[x] = (byte)((scanline[x] + PaethPredicator(left, above, upperLeft)) % 256);
+ res[x] = (byte)((scan[x] + PaethPredicator(left, above, upperLeft)) % 256);
+ }
}
return result;
@@ -44,25 +49,30 @@ namespace ImageSharp.Formats
///
/// The scanline to encode
/// The previous scanline.
+ /// The encoded scanline.
/// The bytes per pixel.
/// The number of bytes per scanline
/// The
- public static byte[] Encode(byte[] scanline, byte[] previousScanline, int bytesPerPixel, int bytesPerScanline)
+ public static byte[] Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel, int bytesPerScanline)
{
// Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp))
- byte[] encodedScanline = new byte[bytesPerScanline + 1];
- encodedScanline[0] = (byte)FilterType.Paeth;
-
- for (int x = 0; x < bytesPerScanline; x++)
+ fixed (byte* scan = scanline)
+ fixed (byte* prev = previousScanline)
+ fixed (byte* res = result)
{
- byte left = (x - bytesPerPixel < 0) ? (byte)0 : scanline[x - bytesPerPixel];
- byte above = previousScanline[x];
- byte upperLeft = (x - bytesPerPixel < 0) ? (byte)0 : previousScanline[x - bytesPerPixel];
+ res[0] = 4;
+
+ for (int x = 0; x < bytesPerScanline; x++)
+ {
+ byte left = (x - bytesPerPixel < 0) ? (byte)0 : scan[x - bytesPerPixel];
+ byte above = prev[x];
+ byte upperLeft = (x - bytesPerPixel < 0) ? (byte)0 : prev[x - bytesPerPixel];
- encodedScanline[x + 1] = (byte)((scanline[x] - PaethPredicator(left, above, upperLeft)) % 256);
+ res[x + 1] = (byte)((scan[x] - PaethPredicator(left, above, upperLeft)) % 256);
+ }
}
- return encodedScanline;
+ return result;
}
///
diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
index c4fbe3e51..63e41a1d5 100644
--- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs
@@ -10,7 +10,7 @@ namespace ImageSharp.Formats
/// of the prior pixel.
///
///
- internal static class SubFilter
+ internal static unsafe class SubFilter
{
///
/// Decodes the scanline
@@ -23,11 +23,15 @@ namespace ImageSharp.Formats
// Sub(x) + Raw(x-bpp)
byte[] result = new byte[scanline.Length];
- for (int x = 1; x < scanline.Length; x++)
+ fixed (byte* scan = scanline)
+ fixed (byte* res = result)
{
- byte priorRawByte = (x - bytesPerPixel < 1) ? (byte)0 : result[x - bytesPerPixel];
+ for (int x = 1; x < scanline.Length; x++)
+ {
+ byte priorRawByte = (x - bytesPerPixel < 1) ? (byte)0 : res[x - bytesPerPixel];
- result[x] = (byte)((scanline[x] + priorRawByte) % 256);
+ res[x] = (byte)((scan[x] + priorRawByte) % 256);
+ }
}
return result;
@@ -37,23 +41,27 @@ namespace ImageSharp.Formats
/// Encodes the scanline
///
/// The scanline to encode
+ /// The encoded scanline.
/// The bytes per pixel.
/// The number of bytes per scanline
/// The
- public static byte[] Encode(byte[] scanline, int bytesPerPixel, int bytesPerScanline)
+ public static byte[] Encode(byte[] scanline, byte[] result, int bytesPerPixel, int bytesPerScanline)
{
// Sub(x) = Raw(x) - Raw(x-bpp)
- byte[] encodedScanline = new byte[bytesPerScanline + 1];
- encodedScanline[0] = (byte)FilterType.Sub;
-
- for (int x = 0; x < bytesPerScanline; x++)
+ fixed (byte* scan = scanline)
+ fixed (byte* res = result)
{
- byte priorRawByte = (x - bytesPerPixel < 0) ? (byte)0 : scanline[x - bytesPerPixel];
+ res[0] = 1;
+
+ for (int x = 0; x < bytesPerScanline; x++)
+ {
+ byte priorRawByte = (x - bytesPerPixel < 0) ? (byte)0 : scan[x - bytesPerPixel];
- encodedScanline[x + 1] = (byte)((scanline[x] - priorRawByte) % 256);
+ res[x + 1] = (byte)((scan[x] - priorRawByte) % 256);
+ }
}
- return encodedScanline;
+ return result;
}
}
}
diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
index 026070421..b40aa944b 100644
--- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
+++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs
@@ -10,7 +10,7 @@ namespace ImageSharp.Formats
/// rather than just to its left, is used as the predictor.
///
///
- internal static class UpFilter
+ internal static unsafe class UpFilter
{
///
/// Decodes the scanline
@@ -23,11 +23,16 @@ namespace ImageSharp.Formats
// Up(x) + Prior(x)
byte[] result = new byte[scanline.Length];
- for (int x = 1; x < scanline.Length; x++)
+ fixed (byte* scan = scanline)
+ fixed (byte* prev = previousScanline)
+ fixed (byte* res = result)
{
- byte above = previousScanline[x];
+ for (int x = 1; x < scanline.Length; x++)
+ {
+ byte above = prev[x];
- result[x] = (byte)((scanline[x] + above) % 256);
+ res[x] = (byte)((scan[x] + above) % 256);
+ }
}
return result;
@@ -37,23 +42,28 @@ namespace ImageSharp.Formats
/// Encodes the scanline
///
/// The scanline to encode
- /// The number of bytes per scanline
/// The previous scanline.
+ /// The encoded scanline.
+ /// The number of bytes per scanline
/// The
- public static byte[] Encode(byte[] scanline, int bytesPerScanline, byte[] previousScanline)
+ public static byte[] Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerScanline)
{
// Up(x) = Raw(x) - Prior(x)
- byte[] encodedScanline = new byte[bytesPerScanline + 1];
- encodedScanline[0] = (byte)FilterType.Up;
-
- for (int x = 0; x < bytesPerScanline; x++)
+ fixed (byte* scan = scanline)
+ fixed (byte* prev = previousScanline)
+ fixed (byte* res = result)
{
- byte above = previousScanline[x];
+ res[0] = 2;
- encodedScanline[x + 1] = (byte)((scanline[x] - above) % 256);
+ for (int x = 0; x < bytesPerScanline; x++)
+ {
+ byte above = prev[x];
+
+ res[x + 1] = (byte)((scan[x] - above) % 256);
+ }
}
- return encodedScanline;
+ return result;
}
}
}
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index cad32d9f2..186e99437 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -283,7 +283,9 @@ namespace ImageSharp.Formats
FilterType filterType = (FilterType)scanline[0];
byte[] defilteredScanline;
- // TODO: It would be good if we can reduce the memory usage here. Each filter is creating a new row.
+ // TODO: It would be good if we can reduce the memory usage here - Each filter is creating a new row.
+ // Every time I try to use the same approach as I have in the encoder though I keep messing up.
+ // Fingers crossed someone with a big brain and a kind heart will come along and finish optimizing this for me.
switch (filterType)
{
case FilterType.None:
@@ -590,6 +592,7 @@ namespace ImageSharp.Formats
/// The chunk.
private void ReadChunkData(PngChunk chunk)
{
+ // TODO: It might be possible to rent this but that could also lead to issues assigning the data to various properties
chunk.Data = new byte[chunk.Length];
this.currentStream.Read(chunk.Data, 0, chunk.Length);
}
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index a06e306f5..764d2f538 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -34,7 +34,6 @@ namespace ImageSharp.Formats
///
private readonly byte[] chunkDataBuffer = new byte[16];
-
///
/// Contains the raw pixel data from an indexed image.
///
@@ -182,7 +181,11 @@ namespace ImageSharp.Formats
this.WritePhysicalChunk(stream, image);
this.WriteGammaChunk(stream);
- this.WriteDataChunks(image, stream);
+ using (PixelAccessor pixels = image.Lock())
+ {
+ this.WriteDataChunks(pixels, stream);
+ }
+
this.WriteEndChunk(stream);
stream.Flush();
}
@@ -249,35 +252,32 @@ namespace ImageSharp.Formats
///
/// The pixel format.
/// The packed format. uint, long, float.
- /// The image to encode.
+ /// The image pixels accessor.
/// The row index.
/// The raw scanline.
- private void CollectGrayscaleBytes(ImageBase image, int row, byte[] rawScanline)
+ private void CollectGrayscaleBytes(PixelAccessor pixels, int row, byte[] rawScanline)
where TColor : struct, IPackedPixel
where TPacked : struct
{
// Copy the pixels across from the image.
// Reuse the chunk type buffer.
- using (PixelAccessor pixels = image.Lock())
+ for (int x = 0; x < this.width; x++)
{
- for (int x = 0; x < this.width; x++)
- {
- // Convert the color to YCbCr and store the luminance
- // Optionally store the original color alpha.
- int offset = x * this.bytesPerPixel;
- pixels[x, row].ToBytes(this.chunkTypeBuffer, 0, ComponentOrder.XYZW);
- byte luminance = (byte)((0.299F * this.chunkTypeBuffer[0]) + (0.587F * this.chunkTypeBuffer[1]) + (0.114F * this.chunkTypeBuffer[2]));
+ // Convert the color to YCbCr and store the luminance
+ // Optionally store the original color alpha.
+ int offset = x * this.bytesPerPixel;
+ pixels[x, row].ToBytes(this.chunkTypeBuffer, 0, ComponentOrder.XYZW);
+ byte luminance = (byte)((0.299F * this.chunkTypeBuffer[0]) + (0.587F * this.chunkTypeBuffer[1]) + (0.114F * this.chunkTypeBuffer[2]));
- for (int i = 0; i < this.bytesPerPixel; i++)
+ for (int i = 0; i < this.bytesPerPixel; i++)
+ {
+ if (i == 0)
+ {
+ rawScanline[offset] = luminance;
+ }
+ else
{
- if (i == 0)
- {
- rawScanline[offset] = luminance;
- }
- else
- {
- rawScanline[offset + i] = this.chunkTypeBuffer[3];
- }
+ rawScanline[offset + i] = this.chunkTypeBuffer[3];
}
}
}
@@ -288,20 +288,17 @@ namespace ImageSharp.Formats
///
/// The pixel format.
/// The packed format. uint, long, float.
- /// The image to encode.
+ /// The image pixel accessor.
/// The row index.
/// The raw scanline.
- private void CollectColorBytes(ImageBase image, int row, byte[] rawScanline)
+ private void CollectColorBytes(PixelAccessor pixels, int row, byte[] rawScanline)
where TColor : struct, IPackedPixel
where TPacked : struct
{
- using (PixelAccessor pixels = image.Lock())
+ int bpp = this.bytesPerPixel;
+ for (int x = 0; x < this.width; x++)
{
- int bpp = this.bytesPerPixel;
- for (int x = 0; x < this.width; x++)
- {
- pixels[x, row].ToBytes(rawScanline, x * this.bytesPerPixel, bpp == 4 ? ComponentOrder.XYZW : ComponentOrder.XYZ);
- }
+ pixels[x, row].ToBytes(rawScanline, x * this.bytesPerPixel, bpp == 4 ? ComponentOrder.XYZW : ComponentOrder.XYZ);
}
}
@@ -311,13 +308,13 @@ namespace ImageSharp.Formats
///
/// The pixel format.
/// The packed format. uint, long, float.
- /// The image to encode.
+ /// The image pixel accessor.
/// The row.
/// The previous scanline.
/// The raw scanline.
+ /// The resultant filtered scanline.
/// The number of bytes per scanline.
- /// The
- private byte[] EncodePixelRow(ImageBase image, int row, byte[] previousScanline, byte[] rawScanline, int bytesPerScanline)
+ private void EncodePixelRow(PixelAccessor pixels, int row, byte[] previousScanline, byte[] rawScanline, byte[] result, int bytesPerScanline)
where TColor : struct, IPackedPixel
where TPacked : struct
{
@@ -328,16 +325,14 @@ namespace ImageSharp.Formats
break;
case PngColorType.Grayscale:
case PngColorType.GrayscaleWithAlpha:
- this.CollectGrayscaleBytes(image, row, rawScanline);
+ this.CollectGrayscaleBytes(pixels, row, rawScanline);
break;
default:
- this.CollectColorBytes(image, row, rawScanline);
+ this.CollectColorBytes(pixels, row, rawScanline);
break;
}
- byte[] filteredScanline = this.GetOptimalFilteredScanline(rawScanline, previousScanline, bytesPerScanline, this.bytesPerPixel);
-
- return filteredScanline;
+ this.GetOptimalFilteredScanline(rawScanline, previousScanline, result, bytesPerScanline);
}
///
@@ -346,37 +341,30 @@ namespace ImageSharp.Formats
///
/// The raw scanline
/// The previous scanline
+ /// The filtered scanline result
/// The number of bytes per scanline
- /// The number of bytes per pixel
- /// The
- private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, int bytesPerScanline, int bytesPerPixel)
+ private void GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, byte[] result, int bytesPerScanline)
{
- Tuple[] candidates;
-
// Palette images don't compress well with adaptive filtering.
if (this.PngColorType == PngColorType.Palette)
{
- candidates = new Tuple[1];
-
- byte[] none = NoneFilter.Encode(rawScanline, bytesPerScanline);
- candidates[0] = new Tuple(none, this.CalculateTotalVariation(none));
+ NoneFilter.Encode(rawScanline, result, bytesPerScanline);
+ return;
}
- else
- {
- candidates = new Tuple[4];
- byte[] sub = SubFilter.Encode(rawScanline, bytesPerPixel, bytesPerScanline);
- candidates[0] = new Tuple(sub, this.CalculateTotalVariation(sub));
+ Tuple[] candidates = new Tuple[4];
- byte[] up = UpFilter.Encode(rawScanline, bytesPerScanline, previousScanline);
- candidates[1] = new Tuple(up, this.CalculateTotalVariation(up));
+ byte[] sub = SubFilter.Encode(rawScanline, result, this.bytesPerPixel, bytesPerScanline);
+ candidates[0] = new Tuple(sub, this.CalculateTotalVariation(sub));
- byte[] average = AverageFilter.Encode(rawScanline, previousScanline, bytesPerPixel, bytesPerScanline);
- candidates[2] = new Tuple(average, this.CalculateTotalVariation(average));
+ byte[] up = UpFilter.Encode(rawScanline, previousScanline, result, bytesPerScanline);
+ candidates[1] = new Tuple(up, this.CalculateTotalVariation(up));
- byte[] paeth = PaethFilter.Encode(rawScanline, previousScanline, bytesPerPixel, bytesPerScanline);
- candidates[3] = new Tuple(paeth, this.CalculateTotalVariation(paeth));
- }
+ byte[] average = AverageFilter.Encode(rawScanline, previousScanline, result, this.bytesPerPixel, bytesPerScanline);
+ candidates[2] = new Tuple(average, this.CalculateTotalVariation(average));
+
+ byte[] paeth = PaethFilter.Encode(rawScanline, previousScanline, result, this.bytesPerPixel, bytesPerScanline);
+ candidates[3] = new Tuple(paeth, this.CalculateTotalVariation(paeth));
int lowestTotalVariation = int.MaxValue;
int lowestTotalVariationIndex = 0;
@@ -390,7 +378,8 @@ namespace ImageSharp.Formats
}
}
- return candidates[lowestTotalVariationIndex].Item1;
+ // ReSharper disable once RedundantAssignment
+ result = candidates[lowestTotalVariationIndex].Item1;
}
///
@@ -592,15 +581,22 @@ namespace ImageSharp.Formats
///
/// The pixel format.
/// The packed format. uint, long, float.
- /// The image to encode.
+ /// The pixel accessor.
/// The stream.
- private void WriteDataChunks(ImageBase image, Stream stream)
+ private void WriteDataChunks(PixelAccessor pixels, Stream stream)
where TColor : struct, IPackedPixel
where TPacked : struct
{
int bytesPerScanline = this.width * this.bytesPerPixel;
byte[] previousScanline = ArrayPool.Shared.Rent(bytesPerScanline);
byte[] rawScanline = ArrayPool.Shared.Rent(bytesPerScanline);
+ int resultLength = bytesPerScanline + 1;
+ byte[] result = ArrayPool.Shared.Rent(resultLength);
+
+ // TODO: Clearing this array makes the visual tests work again when encoding multiple images in a row.
+ // The png analyser tool I use still cannot decompress the image though my own decoder, chome and edge browsers, and paint can. Twitter also cannot read the file.
+ // It's 2am now so I'm going to check in what I have and cry. :'(
+ Array.Clear(result, 0, resultLength);
byte[] buffer;
int bufferLength;
@@ -612,9 +608,8 @@ namespace ImageSharp.Formats
{
for (int y = 0; y < this.height; y++)
{
- byte[] data = this.EncodePixelRow(image, y, previousScanline, rawScanline, bytesPerScanline);
- deflateStream.Write(data, 0, data.Length);
- deflateStream.Flush();
+ this.EncodePixelRow(pixels, y, previousScanline, rawScanline, result, bytesPerScanline);
+ deflateStream.Write(result, 0, resultLength);
// Do a bit of shuffling;
byte[] tmp = rawScanline;
@@ -622,18 +617,20 @@ namespace ImageSharp.Formats
previousScanline = tmp;
}
- bufferLength = (int)memoryStream.Length;
+ deflateStream.Flush();
buffer = memoryStream.ToArray();
+ bufferLength = buffer.Length;
}
}
finally
{
+ memoryStream?.Dispose();
ArrayPool.Shared.Return(previousScanline);
ArrayPool.Shared.Return(rawScanline);
- memoryStream?.Dispose();
+ ArrayPool.Shared.Return(result);
}
- // Store the chunks in repeated 64k blocks.
+ // Store the chunks in repeated 64k blocks.
// This reduces the memory load for decoding the image for many decoders.
int numChunks = bufferLength / MaxBlockSize;
diff --git a/src/ImageSharp/IO/EndianBinaryWriter.cs b/src/ImageSharp/IO/EndianBinaryWriter.cs
index b10ae79b4..c10d118cf 100644
--- a/src/ImageSharp/IO/EndianBinaryWriter.cs
+++ b/src/ImageSharp/IO/EndianBinaryWriter.cs
@@ -346,6 +346,19 @@ namespace ImageSharp.IO
this.BaseStream.Write(this.buffer, 0, index);
}
+ ///
+ /// Disposes of the underlying stream.
+ ///
+ public void Dispose()
+ {
+ if (!this.disposed)
+ {
+ this.Flush();
+ this.disposed = true;
+ ((IDisposable)this.BaseStream).Dispose();
+ }
+ }
+
///
/// Checks whether or not the writer has been disposed, throwing an exception if so.
///
@@ -368,18 +381,5 @@ namespace ImageSharp.IO
this.CheckDisposed();
this.BaseStream.Write(bytes, 0, length);
}
-
- ///
- /// Disposes of the underlying stream.
- ///
- public void Dispose()
- {
- if (!this.disposed)
- {
- this.Flush();
- this.disposed = true;
- ((IDisposable)this.BaseStream).Dispose();
- }
- }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs
index 83a4b9dff..9014f8653 100644
--- a/src/ImageSharp/Image.cs
+++ b/src/ImageSharp/Image.cs
@@ -16,8 +16,7 @@ namespace ImageSharp
public class Image : Image
{
///
- /// Initializes a new instance of the class
- /// with the height and the width of the image.
+ /// Initializes a new instance of the class.
///
public Image()
{
diff --git a/src/ImageSharp/Image/Image.cs b/src/ImageSharp/Image/Image.cs
index 337f38fcf..2915b5365 100644
--- a/src/ImageSharp/Image/Image.cs
+++ b/src/ImageSharp/Image/Image.cs
@@ -191,6 +191,7 @@ namespace ImageSharp
///
/// The stream to save the image to.
/// Thrown if the stream is null.
+ /// The
public Image Save(Stream stream)
{
Guard.NotNull(stream, nameof(stream));
@@ -204,6 +205,7 @@ namespace ImageSharp
/// The stream to save the image to.
/// The format to save the image as.
/// Thrown if the stream is null.
+ /// The
public Image Save(Stream stream, IImageFormat format)
{
Guard.NotNull(stream, nameof(stream));
diff --git a/src/ImageSharp/Numerics/Ellipse.cs b/src/ImageSharp/Numerics/Ellipse.cs
index f464c4b26..9aba745e4 100644
--- a/src/ImageSharp/Numerics/Ellipse.cs
+++ b/src/ImageSharp/Numerics/Ellipse.cs
@@ -9,18 +9,27 @@ namespace ImageSharp
using System.ComponentModel;
using System.Numerics;
+ ///
+ /// Represents an ellipse.
+ ///
public struct Ellipse : IEquatable
{
+ ///
+ /// Represents a that has X and Y values set to zero.
+ ///
+ public static readonly Ellipse Empty = default(Ellipse);
+
///
/// The center point.
///
private Point center;
///
- /// Represents a that has X and Y values set to zero.
+ /// Initializes a new instance of the struct.
///
- public static readonly Ellipse Empty = default(Ellipse);
-
+ /// The center point.
+ /// The x-radius.
+ /// The y-radius.
public Ellipse(Point center, float radiusX, float radiusY)
{
this.center = center;
diff --git a/src/ImageSharp/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Profiles/Exif/ExifReader.cs
index eef6f6f3a..7bbfb3d06 100644
--- a/src/ImageSharp/Profiles/Exif/ExifReader.cs
+++ b/src/ImageSharp/Profiles/Exif/ExifReader.cs
@@ -112,8 +112,16 @@ namespace ImageSharp
return result;
}
+ ///
+ /// Gets the invalid tags.
+ ///
public IEnumerable InvalidTags => this.invalidTags;
+ ///
+ /// Adds the collection of EXIF values to the reader.
+ ///
+ /// The values.
+ /// The index.
private void AddValues(Collection values, uint index)
{
this.currentIndex = this.startIndex + index;
diff --git a/src/ImageSharp/Profiles/Exif/ExifTag.cs b/src/ImageSharp/Profiles/Exif/ExifTag.cs
index 43f725f0c..894f5a064 100644
--- a/src/ImageSharp/Profiles/Exif/ExifTag.cs
+++ b/src/ImageSharp/Profiles/Exif/ExifTag.cs
@@ -3,12 +3,11 @@
// Licensed under the Apache License, Version 2.0.
//
-// Descriptions from: http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
-
namespace ImageSharp
{
///
/// All exif tags from the Exif standard 2.2
+ /// Descriptions from:
///
public enum ExifTag
{
diff --git a/src/ImageSharp/Samplers/Options/Orientation.cs b/src/ImageSharp/Samplers/Options/Orientation.cs
index dfe57a4b0..d7e96ddf4 100644
--- a/src/ImageSharp/Samplers/Options/Orientation.cs
+++ b/src/ImageSharp/Samplers/Options/Orientation.cs
@@ -5,16 +5,54 @@
namespace ImageSharp
{
+ ///
+ /// Enumerates the available orientation values supplied by EXIF metadata.
+ ///
internal enum Orientation : ushort
{
+ ///
+ /// Unknown rotation.
+ ///
Unknown = 0,
+
+ ///
+ /// The 0th row at the top, the 0th column on the left.
+ ///
TopLeft = 1,
+
+ ///
+ /// The 0th row at the top, the 0th column on the right.
+ ///
TopRight = 2,
+
+ ///
+ /// The 0th row at the bottom, the 0th column on the right.
+ ///
BottomRight = 3,
+
+ ///
+ /// The 0th row at the bottom, the 0th column on the left.
+ ///
BottomLeft = 4,
+
+ ///
+ /// The 0th row on the left, the 0th column at the top.
+ ///
LeftTop = 5,
+
+ ///
+ /// The 0th row at the right, the 0th column at the top.
+ ///
RightTop = 6,
+
+ ///
+ /// The 0th row on the right, the 0th column at the bottom.
+ ///
RightBottom = 7,
+
+ ///
+ /// The 0th row on the left, the 0th column at the bottom.
+ ///
LeftBottom = 8
}
}
diff --git a/src/ImageSharp/Samplers/Processors/RotateProcessor.cs b/src/ImageSharp/Samplers/Processors/RotateProcessor.cs
index 0fdc187bd..05cbda36e 100644
--- a/src/ImageSharp/Samplers/Processors/RotateProcessor.cs
+++ b/src/ImageSharp/Samplers/Processors/RotateProcessor.cs
@@ -88,7 +88,7 @@ namespace ImageSharp.Processors
///
/// The target image.
/// The source image.
- ///
+ /// The
private bool OptimizedApply(ImageBase target, ImageBase source)
{
const float Epsilon = .0001F;
diff --git a/src/ImageSharp/Samplers/Processors/SkewProcessor.cs b/src/ImageSharp/Samplers/Processors/SkewProcessor.cs
index b21301a87..1db363a77 100644
--- a/src/ImageSharp/Samplers/Processors/SkewProcessor.cs
+++ b/src/ImageSharp/Samplers/Processors/SkewProcessor.cs
@@ -19,7 +19,7 @@ namespace ImageSharp.Processors
where TPacked : struct
{
///
- /// The tranform matrix to apply.
+ /// The transform matrix to apply.
///
private Matrix3x2 processMatrix;
@@ -38,16 +38,6 @@ namespace ImageSharp.Processors
///
public bool Expand { get; set; } = true;
- ///
- protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
- {
- this.processMatrix = Point.CreateSkew(new Point(0, 0), -this.AngleX, -this.AngleY);
- if (this.Expand)
- {
- CreateNewTarget(target, sourceRectangle, this.processMatrix);
- }
- }
-
///
public override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
@@ -75,5 +65,15 @@ namespace ImageSharp.Processors
});
}
}
+
+ ///
+ protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
+ {
+ this.processMatrix = Point.CreateSkew(new Point(0, 0), -this.AngleX, -this.AngleY);
+ if (this.Expand)
+ {
+ CreateNewTarget(target, sourceRectangle, this.processMatrix);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs b/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs
index 7e2060ce9..261a7f269 100644
--- a/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs
+++ b/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs
@@ -32,7 +32,7 @@ namespace ImageSharp.Benchmarks.Image
}
[Benchmark(Baseline = true, Description = "System.Drawing Bmp")]
- public void JpegSystemDrawing()
+ public void BmpSystemDrawing()
{
using (MemoryStream memoryStream = new MemoryStream())
{
@@ -41,7 +41,7 @@ namespace ImageSharp.Benchmarks.Image
}
[Benchmark(Description = "ImageSharp Bmp")]
- public void JpegCore()
+ public void BmpCore()
{
using (MemoryStream memoryStream = new MemoryStream())
{
diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs b/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs
index 7128089f9..e2acb4db5 100644
--- a/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs
+++ b/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs
@@ -32,7 +32,7 @@ namespace ImageSharp.Benchmarks.Image
}
[Benchmark(Baseline = true, Description = "System.Drawing Gif")]
- public void JpegSystemDrawing()
+ public void GifSystemDrawing()
{
using (MemoryStream memoryStream = new MemoryStream())
{
@@ -41,7 +41,7 @@ namespace ImageSharp.Benchmarks.Image
}
[Benchmark(Description = "ImageSharp Gif")]
- public void JpegCore()
+ public void GifCore()
{
using (MemoryStream memoryStream = new MemoryStream())
{
diff --git a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs
index d9a83f556..52d953775 100644
--- a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs
+++ b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs
@@ -32,7 +32,7 @@ namespace ImageSharp.Benchmarks.Image
}
[Benchmark(Baseline = true, Description = "System.Drawing Png")]
- public void JpegSystemDrawing()
+ public void PngSystemDrawing()
{
using (MemoryStream memoryStream = new MemoryStream())
{
@@ -41,7 +41,7 @@ namespace ImageSharp.Benchmarks.Image
}
[Benchmark(Description = "ImageSharp Png")]
- public void JpegCore()
+ public void PngCore()
{
using (MemoryStream memoryStream = new MemoryStream())
{