Browse Source

Merge branch 'master' into HEAD

pull/34/head
Anton Firszov 10 years ago
parent
commit
44d7359ae5
  1. 2
      Settings.StyleCop
  2. 20
      src/ImageSharp/Common/Extensions/StreamExtensions.cs
  3. 3
      src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
  4. 43
      src/ImageSharp/Formats/Png/Filters/AverageFilter.cs
  5. 11
      src/ImageSharp/Formats/Png/Filters/NoneFilter.cs
  6. 42
      src/ImageSharp/Formats/Png/Filters/PaethFilter.cs
  7. 32
      src/ImageSharp/Formats/Png/Filters/SubFilter.cs
  8. 36
      src/ImageSharp/Formats/Png/Filters/UpFilter.cs
  9. 5
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  10. 131
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  11. 26
      src/ImageSharp/IO/EndianBinaryWriter.cs
  12. 3
      src/ImageSharp/Image.cs
  13. 2
      src/ImageSharp/Image/Image.cs
  14. 15
      src/ImageSharp/Numerics/Ellipse.cs
  15. 8
      src/ImageSharp/Profiles/Exif/ExifReader.cs
  16. 3
      src/ImageSharp/Profiles/Exif/ExifTag.cs
  17. 38
      src/ImageSharp/Samplers/Options/Orientation.cs
  18. 2
      src/ImageSharp/Samplers/Processors/RotateProcessor.cs
  19. 22
      src/ImageSharp/Samplers/Processors/SkewProcessor.cs
  20. 4
      tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs
  21. 4
      tests/ImageSharp.Benchmarks/Image/EncodeGif.cs
  22. 4
      tests/ImageSharp.Benchmarks/Image/EncodePng.cs

2
Settings.StyleCop

@ -32,6 +32,8 @@
<Value>Vol</Value> <Value>Vol</Value>
<Value>pp</Value> <Value>pp</Value>
<Value>cmyk</Value> <Value>cmyk</Value>
<Value>Paeth</Value>
<Value>th</Value>
</CollectionProperty> </CollectionProperty>
</GlobalSettings> </GlobalSettings>
<Analyzers> <Analyzers>

20
src/ImageSharp/Common/Extensions/StreamExtensions.cs

@ -5,10 +5,19 @@
namespace ImageSharp namespace ImageSharp
{ {
using System.Buffers;
using System.IO; using System.IO;
/// <summary>
/// Extension methods for the <see cref="Stream"/> type.
/// </summary>
internal static class StreamExtensions internal static class StreamExtensions
{ {
/// <summary>
/// Skips the number of bytes in the given stream.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="count">The count.</param>
public static void Skip(this Stream stream, int count) public static void Skip(this Stream stream, int count)
{ {
if (count < 1) if (count < 1)
@ -22,8 +31,15 @@ namespace ImageSharp
} }
else else
{ {
byte[] foo = new byte[count]; byte[] foo = ArrayPool<byte>.Shared.Rent(count);
stream.Read(foo, 0, count); try
{
stream.Read(foo, 0, count);
}
finally
{
ArrayPool<byte>.Shared.Return(foo);
}
} }
} }
} }

3
src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs

@ -246,7 +246,6 @@ namespace ImageSharp.Formats
/// <summary> /// <summary>
/// The AC luminance huffman table index /// The AC luminance huffman table index
/// </summary> /// </summary>
LuminanceAC = 1, LuminanceAC = 1,
// ReSharper restore UnusedMember.Local // ReSharper restore UnusedMember.Local
@ -851,6 +850,7 @@ namespace ImageSharp.Formats
Block b = new Block(); Block b = new Block();
Block cb = new Block(); Block cb = new Block();
Block cr = new Block(); Block cr = new Block();
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;
@ -880,6 +880,7 @@ namespace ImageSharp.Formats
Block b = new Block(); Block b = new Block();
Block[] cb = new Block[4]; Block[] cb = new Block[4];
Block[] cr = new Block[4]; Block[] cr = new Block[4];
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; int prevDCY = 0, prevDCCb = 0, prevDCCr = 0;

43
src/ImageSharp/Formats/Png/Filters/AverageFilter.cs

@ -12,7 +12,7 @@ namespace ImageSharp.Formats
/// the value of a pixel. /// the value of a pixel.
/// <see href="https://www.w3.org/TR/PNG-Filters.html"/> /// <see href="https://www.w3.org/TR/PNG-Filters.html"/>
/// </summary> /// </summary>
internal static class AverageFilter internal static unsafe class AverageFilter
{ {
/// <summary> /// <summary>
/// Decodes the scanline /// Decodes the scanline
@ -28,12 +28,17 @@ namespace ImageSharp.Formats
// Average(x) + floor((Raw(x-bpp)+Prior(x))/2) // Average(x) + floor((Raw(x-bpp)+Prior(x))/2)
byte[] result = new byte[scanline.Length]; 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]; for (int x = 1; x < scanline.Length; x++)
byte above = previousScanline[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; return result;
@ -44,25 +49,29 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <param name="scanline">The scanline to encode</param> /// <param name="scanline">The scanline to encode</param>
/// <param name="previousScanline">The previous scanline.</param> /// <param name="previousScanline">The previous scanline.</param>
/// <param name="result">The encoded scanline.</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param> /// <param name="bytesPerPixel">The bytes per pixel.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param> /// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <returns>The <see cref="T:byte[]"/></returns> /// <returns>The <see cref="T:byte[]"/></returns>
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) // Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2)
byte[] encodedScanline = new byte[bytesPerScanline + 1]; fixed (byte* scan = scanline)
fixed (byte* prev = previousScanline)
encodedScanline[0] = (byte)FilterType.Average; fixed (byte* res = result)
for (int x = 0; x < bytesPerScanline; x++)
{ {
byte left = (x - bytesPerPixel < 0) ? (byte)0 : scanline[x - bytesPerPixel]; res[0] = 3;
byte above = previousScanline[x];
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;
} }
/// <summary> /// <summary>
@ -73,7 +82,7 @@ namespace ImageSharp.Formats
/// <returns>The <see cref="int"/></returns> /// <returns>The <see cref="int"/></returns>
private static int Average(byte left, byte above) private static int Average(byte left, byte above)
{ {
return Convert.ToInt32(Math.Floor((left + above) / 2.0D)); return (left + above) >> 1;
} }
} }
} }

11
src/ImageSharp/Formats/Png/Filters/NoneFilter.cs

@ -29,16 +29,13 @@ namespace ImageSharp.Formats
/// Encodes the scanline /// Encodes the scanline
/// </summary> /// </summary>
/// <param name="scanline">The scanline to encode</param> /// <param name="scanline">The scanline to encode</param>
/// <param name="result">The encoded scanline.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param> /// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <returns>The <see cref="T:byte[]"/></returns> public static void Encode(byte[] scanline, byte[] result, int bytesPerScanline)
public static byte[] Encode(byte[] scanline, int bytesPerScanline)
{ {
// Insert a byte before the data. // Insert a byte before the data.
byte[] encodedScanline = new byte[bytesPerScanline + 1]; result[0] = 0;
encodedScanline[0] = (byte)FilterType.None; Buffer.BlockCopy(scanline, 0, result, 1, bytesPerScanline);
Buffer.BlockCopy(scanline, 0, encodedScanline, 1, bytesPerScanline);
return encodedScanline;
} }
} }
} }

42
src/ImageSharp/Formats/Png/Filters/PaethFilter.cs

@ -13,7 +13,7 @@ namespace ImageSharp.Formats
/// This technique is due to Alan W. Paeth. /// This technique is due to Alan W. Paeth.
/// <see href="https://www.w3.org/TR/PNG-Filters.html"/> /// <see href="https://www.w3.org/TR/PNG-Filters.html"/>
/// </summary> /// </summary>
internal static class PaethFilter internal static unsafe class PaethFilter
{ {
/// <summary> /// <summary>
/// Decodes the scanline /// Decodes the scanline
@ -27,13 +27,18 @@ namespace ImageSharp.Formats
// Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp)) // Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp))
byte[] result = new byte[scanline.Length]; 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]; for (int x = 1; x < scanline.Length; x++)
byte above = previousScanline[x]; {
byte upperLeft = (x - bytesPerPixel < 1) ? (byte)0 : previousScanline[x - bytesPerPixel]; 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; return result;
@ -44,25 +49,30 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <param name="scanline">The scanline to encode</param> /// <param name="scanline">The scanline to encode</param>
/// <param name="previousScanline">The previous scanline.</param> /// <param name="previousScanline">The previous scanline.</param>
/// <param name="result">The encoded scanline.</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param> /// <param name="bytesPerPixel">The bytes per pixel.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param> /// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <returns>The <see cref="T:byte[]"/></returns> /// <returns>The <see cref="T:byte[]"/></returns>
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)) // Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp))
byte[] encodedScanline = new byte[bytesPerScanline + 1]; fixed (byte* scan = scanline)
encodedScanline[0] = (byte)FilterType.Paeth; fixed (byte* prev = previousScanline)
fixed (byte* res = result)
for (int x = 0; x < bytesPerScanline; x++)
{ {
byte left = (x - bytesPerPixel < 0) ? (byte)0 : scanline[x - bytesPerPixel]; res[0] = 4;
byte above = previousScanline[x];
byte upperLeft = (x - bytesPerPixel < 0) ? (byte)0 : previousScanline[x - bytesPerPixel]; 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;
} }
/// <summary> /// <summary>

32
src/ImageSharp/Formats/Png/Filters/SubFilter.cs

@ -10,7 +10,7 @@ namespace ImageSharp.Formats
/// of the prior pixel. /// of the prior pixel.
/// <see href="https://www.w3.org/TR/PNG-Filters.html"/> /// <see href="https://www.w3.org/TR/PNG-Filters.html"/>
/// </summary> /// </summary>
internal static class SubFilter internal static unsafe class SubFilter
{ {
/// <summary> /// <summary>
/// Decodes the scanline /// Decodes the scanline
@ -23,11 +23,15 @@ namespace ImageSharp.Formats
// Sub(x) + Raw(x-bpp) // Sub(x) + Raw(x-bpp)
byte[] result = new byte[scanline.Length]; 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; return result;
@ -37,23 +41,27 @@ namespace ImageSharp.Formats
/// Encodes the scanline /// Encodes the scanline
/// </summary> /// </summary>
/// <param name="scanline">The scanline to encode</param> /// <param name="scanline">The scanline to encode</param>
/// <param name="result">The encoded scanline.</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param> /// <param name="bytesPerPixel">The bytes per pixel.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param> /// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <returns>The <see cref="T:byte[]"/></returns> /// <returns>The <see cref="T:byte[]"/></returns>
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) // Sub(x) = Raw(x) - Raw(x-bpp)
byte[] encodedScanline = new byte[bytesPerScanline + 1]; fixed (byte* scan = scanline)
encodedScanline[0] = (byte)FilterType.Sub; fixed (byte* res = result)
for (int x = 0; x < bytesPerScanline; x++)
{ {
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;
} }
} }
} }

36
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. /// rather than just to its left, is used as the predictor.
/// <see href="https://www.w3.org/TR/PNG-Filters.html"/> /// <see href="https://www.w3.org/TR/PNG-Filters.html"/>
/// </summary> /// </summary>
internal static class UpFilter internal static unsafe class UpFilter
{ {
/// <summary> /// <summary>
/// Decodes the scanline /// Decodes the scanline
@ -23,11 +23,16 @@ namespace ImageSharp.Formats
// Up(x) + Prior(x) // Up(x) + Prior(x)
byte[] result = new byte[scanline.Length]; 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; return result;
@ -37,23 +42,28 @@ namespace ImageSharp.Formats
/// Encodes the scanline /// Encodes the scanline
/// </summary> /// </summary>
/// <param name="scanline">The scanline to encode</param> /// <param name="scanline">The scanline to encode</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <param name="previousScanline">The previous scanline.</param> /// <param name="previousScanline">The previous scanline.</param>
/// <param name="result">The encoded scanline.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <returns>The <see cref="T:byte[]"/></returns> /// <returns>The <see cref="T:byte[]"/></returns>
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) // Up(x) = Raw(x) - Prior(x)
byte[] encodedScanline = new byte[bytesPerScanline + 1]; fixed (byte* scan = scanline)
encodedScanline[0] = (byte)FilterType.Up; fixed (byte* prev = previousScanline)
fixed (byte* res = result)
for (int x = 0; x < bytesPerScanline; x++)
{ {
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;
} }
} }
} }

5
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -283,7 +283,9 @@ namespace ImageSharp.Formats
FilterType filterType = (FilterType)scanline[0]; FilterType filterType = (FilterType)scanline[0];
byte[] defilteredScanline; 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) switch (filterType)
{ {
case FilterType.None: case FilterType.None:
@ -590,6 +592,7 @@ namespace ImageSharp.Formats
/// <param name="chunk">The chunk.</param> /// <param name="chunk">The chunk.</param>
private void ReadChunkData(PngChunk 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]; chunk.Data = new byte[chunk.Length];
this.currentStream.Read(chunk.Data, 0, chunk.Length); this.currentStream.Read(chunk.Data, 0, chunk.Length);
} }

131
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -34,7 +34,6 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
private readonly byte[] chunkDataBuffer = new byte[16]; private readonly byte[] chunkDataBuffer = new byte[16];
/// <summary> /// <summary>
/// Contains the raw pixel data from an indexed image. /// Contains the raw pixel data from an indexed image.
/// </summary> /// </summary>
@ -182,7 +181,11 @@ namespace ImageSharp.Formats
this.WritePhysicalChunk(stream, image); this.WritePhysicalChunk(stream, image);
this.WriteGammaChunk(stream); this.WriteGammaChunk(stream);
this.WriteDataChunks(image, stream); using (PixelAccessor<TColor, TPacked> pixels = image.Lock())
{
this.WriteDataChunks(pixels, stream);
}
this.WriteEndChunk(stream); this.WriteEndChunk(stream);
stream.Flush(); stream.Flush();
} }
@ -249,35 +252,32 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="image">The image to encode.</param> /// <param name="pixels">The image pixels accessor.</param>
/// <param name="row">The row index.</param> /// <param name="row">The row index.</param>
/// <param name="rawScanline">The raw scanline.</param> /// <param name="rawScanline">The raw scanline.</param>
private void CollectGrayscaleBytes<TColor, TPacked>(ImageBase<TColor, TPacked> image, int row, byte[] rawScanline) private void CollectGrayscaleBytes<TColor, TPacked>(PixelAccessor<TColor, TPacked> pixels, int row, byte[] rawScanline)
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct where TPacked : struct
{ {
// Copy the pixels across from the image. // Copy the pixels across from the image.
// Reuse the chunk type buffer. // Reuse the chunk type buffer.
using (PixelAccessor<TColor, TPacked> 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.
// Convert the color to YCbCr and store the luminance int offset = x * this.bytesPerPixel;
// Optionally store the original color alpha. pixels[x, row].ToBytes(this.chunkTypeBuffer, 0, ComponentOrder.XYZW);
int offset = x * this.bytesPerPixel; byte luminance = (byte)((0.299F * this.chunkTypeBuffer[0]) + (0.587F * this.chunkTypeBuffer[1]) + (0.114F * this.chunkTypeBuffer[2]));
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 + i] = this.chunkTypeBuffer[3];
{
rawScanline[offset] = luminance;
}
else
{
rawScanline[offset + i] = this.chunkTypeBuffer[3];
}
} }
} }
} }
@ -288,20 +288,17 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="image">The image to encode.</param> /// <param name="pixels">The image pixel accessor.</param>
/// <param name="row">The row index.</param> /// <param name="row">The row index.</param>
/// <param name="rawScanline">The raw scanline.</param> /// <param name="rawScanline">The raw scanline.</param>
private void CollectColorBytes<TColor, TPacked>(ImageBase<TColor, TPacked> image, int row, byte[] rawScanline) private void CollectColorBytes<TColor, TPacked>(PixelAccessor<TColor, TPacked> pixels, int row, byte[] rawScanline)
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct where TPacked : struct
{ {
using (PixelAccessor<TColor, TPacked> pixels = image.Lock()) int bpp = this.bytesPerPixel;
for (int x = 0; x < this.width; x++)
{ {
int bpp = this.bytesPerPixel; pixels[x, row].ToBytes(rawScanline, x * this.bytesPerPixel, bpp == 4 ? ComponentOrder.XYZW : ComponentOrder.XYZ);
for (int x = 0; x < this.width; x++)
{
pixels[x, row].ToBytes(rawScanline, x * this.bytesPerPixel, bpp == 4 ? ComponentOrder.XYZW : ComponentOrder.XYZ);
}
} }
} }
@ -311,13 +308,13 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="image">The image to encode.</param> /// <param name="pixels">The image pixel accessor.</param>
/// <param name="row">The row.</param> /// <param name="row">The row.</param>
/// <param name="previousScanline">The previous scanline.</param> /// <param name="previousScanline">The previous scanline.</param>
/// <param name="rawScanline">The raw scanline.</param> /// <param name="rawScanline">The raw scanline.</param>
/// <param name="result">The resultant filtered scanline.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline.</param> /// <param name="bytesPerScanline">The number of bytes per scanline.</param>
/// <returns>The <see cref="T:byte[]"/></returns> private void EncodePixelRow<TColor, TPacked>(PixelAccessor<TColor, TPacked> pixels, int row, byte[] previousScanline, byte[] rawScanline, byte[] result, int bytesPerScanline)
private byte[] EncodePixelRow<TColor, TPacked>(ImageBase<TColor, TPacked> image, int row, byte[] previousScanline, byte[] rawScanline, int bytesPerScanline)
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct where TPacked : struct
{ {
@ -328,16 +325,14 @@ namespace ImageSharp.Formats
break; break;
case PngColorType.Grayscale: case PngColorType.Grayscale:
case PngColorType.GrayscaleWithAlpha: case PngColorType.GrayscaleWithAlpha:
this.CollectGrayscaleBytes(image, row, rawScanline); this.CollectGrayscaleBytes(pixels, row, rawScanline);
break; break;
default: default:
this.CollectColorBytes(image, row, rawScanline); this.CollectColorBytes(pixels, row, rawScanline);
break; break;
} }
byte[] filteredScanline = this.GetOptimalFilteredScanline(rawScanline, previousScanline, bytesPerScanline, this.bytesPerPixel); this.GetOptimalFilteredScanline(rawScanline, previousScanline, result, bytesPerScanline);
return filteredScanline;
} }
/// <summary> /// <summary>
@ -346,37 +341,30 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <param name="rawScanline">The raw scanline</param> /// <param name="rawScanline">The raw scanline</param>
/// <param name="previousScanline">The previous scanline</param> /// <param name="previousScanline">The previous scanline</param>
/// <param name="result">The filtered scanline result</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param> /// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <param name="bytesPerPixel">The number of bytes per pixel</param> private void GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, byte[] result, int bytesPerScanline)
/// <returns>The <see cref="T:byte[]"/></returns>
private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, int bytesPerScanline, int bytesPerPixel)
{ {
Tuple<byte[], int>[] candidates;
// Palette images don't compress well with adaptive filtering. // Palette images don't compress well with adaptive filtering.
if (this.PngColorType == PngColorType.Palette) if (this.PngColorType == PngColorType.Palette)
{ {
candidates = new Tuple<byte[], int>[1]; NoneFilter.Encode(rawScanline, result, bytesPerScanline);
return;
byte[] none = NoneFilter.Encode(rawScanline, bytesPerScanline);
candidates[0] = new Tuple<byte[], int>(none, this.CalculateTotalVariation(none));
} }
else
{
candidates = new Tuple<byte[], int>[4];
byte[] sub = SubFilter.Encode(rawScanline, bytesPerPixel, bytesPerScanline); Tuple<byte[], int>[] candidates = new Tuple<byte[], int>[4];
candidates[0] = new Tuple<byte[], int>(sub, this.CalculateTotalVariation(sub));
byte[] up = UpFilter.Encode(rawScanline, bytesPerScanline, previousScanline); byte[] sub = SubFilter.Encode(rawScanline, result, this.bytesPerPixel, bytesPerScanline);
candidates[1] = new Tuple<byte[], int>(up, this.CalculateTotalVariation(up)); candidates[0] = new Tuple<byte[], int>(sub, this.CalculateTotalVariation(sub));
byte[] average = AverageFilter.Encode(rawScanline, previousScanline, bytesPerPixel, bytesPerScanline); byte[] up = UpFilter.Encode(rawScanline, previousScanline, result, bytesPerScanline);
candidates[2] = new Tuple<byte[], int>(average, this.CalculateTotalVariation(average)); candidates[1] = new Tuple<byte[], int>(up, this.CalculateTotalVariation(up));
byte[] paeth = PaethFilter.Encode(rawScanline, previousScanline, bytesPerPixel, bytesPerScanline); byte[] average = AverageFilter.Encode(rawScanline, previousScanline, result, this.bytesPerPixel, bytesPerScanline);
candidates[3] = new Tuple<byte[], int>(paeth, this.CalculateTotalVariation(paeth)); candidates[2] = new Tuple<byte[], int>(average, this.CalculateTotalVariation(average));
}
byte[] paeth = PaethFilter.Encode(rawScanline, previousScanline, result, this.bytesPerPixel, bytesPerScanline);
candidates[3] = new Tuple<byte[], int>(paeth, this.CalculateTotalVariation(paeth));
int lowestTotalVariation = int.MaxValue; int lowestTotalVariation = int.MaxValue;
int lowestTotalVariationIndex = 0; int lowestTotalVariationIndex = 0;
@ -390,7 +378,8 @@ namespace ImageSharp.Formats
} }
} }
return candidates[lowestTotalVariationIndex].Item1; // ReSharper disable once RedundantAssignment
result = candidates[lowestTotalVariationIndex].Item1;
} }
/// <summary> /// <summary>
@ -592,15 +581,22 @@ namespace ImageSharp.Formats
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
/// <param name="image">The image to encode.</param> /// <param name="pixels">The pixel accessor.</param>
/// <param name="stream">The stream.</param> /// <param name="stream">The stream.</param>
private void WriteDataChunks<TColor, TPacked>(ImageBase<TColor, TPacked> image, Stream stream) private void WriteDataChunks<TColor, TPacked>(PixelAccessor<TColor, TPacked> pixels, Stream stream)
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct where TPacked : struct
{ {
int bytesPerScanline = this.width * this.bytesPerPixel; int bytesPerScanline = this.width * this.bytesPerPixel;
byte[] previousScanline = ArrayPool<byte>.Shared.Rent(bytesPerScanline); byte[] previousScanline = ArrayPool<byte>.Shared.Rent(bytesPerScanline);
byte[] rawScanline = ArrayPool<byte>.Shared.Rent(bytesPerScanline); byte[] rawScanline = ArrayPool<byte>.Shared.Rent(bytesPerScanline);
int resultLength = bytesPerScanline + 1;
byte[] result = ArrayPool<byte>.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; byte[] buffer;
int bufferLength; int bufferLength;
@ -612,9 +608,8 @@ namespace ImageSharp.Formats
{ {
for (int y = 0; y < this.height; y++) for (int y = 0; y < this.height; y++)
{ {
byte[] data = this.EncodePixelRow(image, y, previousScanline, rawScanline, bytesPerScanline); this.EncodePixelRow(pixels, y, previousScanline, rawScanline, result, bytesPerScanline);
deflateStream.Write(data, 0, data.Length); deflateStream.Write(result, 0, resultLength);
deflateStream.Flush();
// Do a bit of shuffling; // Do a bit of shuffling;
byte[] tmp = rawScanline; byte[] tmp = rawScanline;
@ -622,18 +617,20 @@ namespace ImageSharp.Formats
previousScanline = tmp; previousScanline = tmp;
} }
bufferLength = (int)memoryStream.Length; deflateStream.Flush();
buffer = memoryStream.ToArray(); buffer = memoryStream.ToArray();
bufferLength = buffer.Length;
} }
} }
finally finally
{ {
memoryStream?.Dispose();
ArrayPool<byte>.Shared.Return(previousScanline); ArrayPool<byte>.Shared.Return(previousScanline);
ArrayPool<byte>.Shared.Return(rawScanline); ArrayPool<byte>.Shared.Return(rawScanline);
memoryStream?.Dispose(); ArrayPool<byte>.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. // This reduces the memory load for decoding the image for many decoders.
int numChunks = bufferLength / MaxBlockSize; int numChunks = bufferLength / MaxBlockSize;

26
src/ImageSharp/IO/EndianBinaryWriter.cs

@ -346,6 +346,19 @@ namespace ImageSharp.IO
this.BaseStream.Write(this.buffer, 0, index); this.BaseStream.Write(this.buffer, 0, index);
} }
/// <summary>
/// Disposes of the underlying stream.
/// </summary>
public void Dispose()
{
if (!this.disposed)
{
this.Flush();
this.disposed = true;
((IDisposable)this.BaseStream).Dispose();
}
}
/// <summary> /// <summary>
/// Checks whether or not the writer has been disposed, throwing an exception if so. /// Checks whether or not the writer has been disposed, throwing an exception if so.
/// </summary> /// </summary>
@ -368,18 +381,5 @@ namespace ImageSharp.IO
this.CheckDisposed(); this.CheckDisposed();
this.BaseStream.Write(bytes, 0, length); this.BaseStream.Write(bytes, 0, length);
} }
/// <summary>
/// Disposes of the underlying stream.
/// </summary>
public void Dispose()
{
if (!this.disposed)
{
this.Flush();
this.disposed = true;
((IDisposable)this.BaseStream).Dispose();
}
}
} }
} }

3
src/ImageSharp/Image.cs

@ -16,8 +16,7 @@ namespace ImageSharp
public class Image : Image<Color, uint> public class Image : Image<Color, uint>
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Image"/> class /// Initializes a new instance of the <see cref="Image"/> class.
/// with the height and the width of the image.
/// </summary> /// </summary>
public Image() public Image()
{ {

2
src/ImageSharp/Image/Image.cs

@ -191,6 +191,7 @@ namespace ImageSharp
/// </summary> /// </summary>
/// <param name="stream">The stream to save the image to.</param> /// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>The <see cref="Image{TColor,TPacked}"/></returns>
public Image<TColor, TPacked> Save(Stream stream) public Image<TColor, TPacked> Save(Stream stream)
{ {
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
@ -204,6 +205,7 @@ namespace ImageSharp
/// <param name="stream">The stream to save the image to.</param> /// <param name="stream">The stream to save the image to.</param>
/// <param name="format">The format to save the image as.</param> /// <param name="format">The format to save the image as.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>The <see cref="Image{TColor,TPacked}"/></returns>
public Image<TColor, TPacked> Save(Stream stream, IImageFormat format) public Image<TColor, TPacked> Save(Stream stream, IImageFormat format)
{ {
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));

15
src/ImageSharp/Numerics/Ellipse.cs

@ -9,18 +9,27 @@ namespace ImageSharp
using System.ComponentModel; using System.ComponentModel;
using System.Numerics; using System.Numerics;
/// <summary>
/// Represents an ellipse.
/// </summary>
public struct Ellipse : IEquatable<Ellipse> public struct Ellipse : IEquatable<Ellipse>
{ {
/// <summary>
/// Represents a <see cref="Ellipse"/> that has X and Y values set to zero.
/// </summary>
public static readonly Ellipse Empty = default(Ellipse);
/// <summary> /// <summary>
/// The center point. /// The center point.
/// </summary> /// </summary>
private Point center; private Point center;
/// <summary> /// <summary>
/// Represents a <see cref="Ellipse"/> that has X and Y values set to zero. /// Initializes a new instance of the <see cref="Ellipse"/> struct.
/// </summary> /// </summary>
public static readonly Ellipse Empty = default(Ellipse); /// <param name="center">The center point.</param>
/// <param name="radiusX">The x-radius.</param>
/// <param name="radiusY">The y-radius.</param>
public Ellipse(Point center, float radiusX, float radiusY) public Ellipse(Point center, float radiusX, float radiusY)
{ {
this.center = center; this.center = center;

8
src/ImageSharp/Profiles/Exif/ExifReader.cs

@ -112,8 +112,16 @@ namespace ImageSharp
return result; return result;
} }
/// <summary>
/// Gets the invalid tags.
/// </summary>
public IEnumerable<ExifTag> InvalidTags => this.invalidTags; public IEnumerable<ExifTag> InvalidTags => this.invalidTags;
/// <summary>
/// Adds the collection of EXIF values to the reader.
/// </summary>
/// <param name="values">The values.</param>
/// <param name="index">The index.</param>
private void AddValues(Collection<ExifValue> values, uint index) private void AddValues(Collection<ExifValue> values, uint index)
{ {
this.currentIndex = this.startIndex + index; this.currentIndex = this.startIndex + index;

3
src/ImageSharp/Profiles/Exif/ExifTag.cs

@ -3,12 +3,11 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
// Descriptions from: http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
namespace ImageSharp namespace ImageSharp
{ {
/// <summary> /// <summary>
/// All exif tags from the Exif standard 2.2 /// All exif tags from the Exif standard 2.2
/// Descriptions from: <see href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html"/>
/// </summary> /// </summary>
public enum ExifTag public enum ExifTag
{ {

38
src/ImageSharp/Samplers/Options/Orientation.cs

@ -5,16 +5,54 @@
namespace ImageSharp namespace ImageSharp
{ {
/// <summary>
/// Enumerates the available orientation values supplied by EXIF metadata.
/// </summary>
internal enum Orientation : ushort internal enum Orientation : ushort
{ {
/// <summary>
/// Unknown rotation.
/// </summary>
Unknown = 0, Unknown = 0,
/// <summary>
/// The 0th row at the top, the 0th column on the left.
/// </summary>
TopLeft = 1, TopLeft = 1,
/// <summary>
/// The 0th row at the top, the 0th column on the right.
/// </summary>
TopRight = 2, TopRight = 2,
/// <summary>
/// The 0th row at the bottom, the 0th column on the right.
/// </summary>
BottomRight = 3, BottomRight = 3,
/// <summary>
/// The 0th row at the bottom, the 0th column on the left.
/// </summary>
BottomLeft = 4, BottomLeft = 4,
/// <summary>
/// The 0th row on the left, the 0th column at the top.
/// </summary>
LeftTop = 5, LeftTop = 5,
/// <summary>
/// The 0th row at the right, the 0th column at the top.
/// </summary>
RightTop = 6, RightTop = 6,
/// <summary>
/// The 0th row on the right, the 0th column at the bottom.
/// </summary>
RightBottom = 7, RightBottom = 7,
/// <summary>
/// The 0th row on the left, the 0th column at the bottom.
/// </summary>
LeftBottom = 8 LeftBottom = 8
} }
} }

2
src/ImageSharp/Samplers/Processors/RotateProcessor.cs

@ -88,7 +88,7 @@ namespace ImageSharp.Processors
/// </summary> /// </summary>
/// <param name="target">The target image.</param> /// <param name="target">The target image.</param>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <returns></returns> /// <returns>The <see cref="bool"/></returns>
private bool OptimizedApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source) private bool OptimizedApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source)
{ {
const float Epsilon = .0001F; const float Epsilon = .0001F;

22
src/ImageSharp/Samplers/Processors/SkewProcessor.cs

@ -19,7 +19,7 @@ namespace ImageSharp.Processors
where TPacked : struct where TPacked : struct
{ {
/// <summary> /// <summary>
/// The tranform matrix to apply. /// The transform matrix to apply.
/// </summary> /// </summary>
private Matrix3x2 processMatrix; private Matrix3x2 processMatrix;
@ -38,16 +38,6 @@ namespace ImageSharp.Processors
/// </summary> /// </summary>
public bool Expand { get; set; } = true; public bool Expand { get; set; } = true;
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> 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);
}
}
/// <inheritdoc/> /// <inheritdoc/>
public override void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) public override void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
@ -75,5 +65,15 @@ namespace ImageSharp.Processors
}); });
} }
} }
/// <inheritdoc/>
protected override void OnApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> 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);
}
}
} }
} }

4
tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs

@ -32,7 +32,7 @@ namespace ImageSharp.Benchmarks.Image
} }
[Benchmark(Baseline = true, Description = "System.Drawing Bmp")] [Benchmark(Baseline = true, Description = "System.Drawing Bmp")]
public void JpegSystemDrawing() public void BmpSystemDrawing()
{ {
using (MemoryStream memoryStream = new MemoryStream()) using (MemoryStream memoryStream = new MemoryStream())
{ {
@ -41,7 +41,7 @@ namespace ImageSharp.Benchmarks.Image
} }
[Benchmark(Description = "ImageSharp Bmp")] [Benchmark(Description = "ImageSharp Bmp")]
public void JpegCore() public void BmpCore()
{ {
using (MemoryStream memoryStream = new MemoryStream()) using (MemoryStream memoryStream = new MemoryStream())
{ {

4
tests/ImageSharp.Benchmarks/Image/EncodeGif.cs

@ -32,7 +32,7 @@ namespace ImageSharp.Benchmarks.Image
} }
[Benchmark(Baseline = true, Description = "System.Drawing Gif")] [Benchmark(Baseline = true, Description = "System.Drawing Gif")]
public void JpegSystemDrawing() public void GifSystemDrawing()
{ {
using (MemoryStream memoryStream = new MemoryStream()) using (MemoryStream memoryStream = new MemoryStream())
{ {
@ -41,7 +41,7 @@ namespace ImageSharp.Benchmarks.Image
} }
[Benchmark(Description = "ImageSharp Gif")] [Benchmark(Description = "ImageSharp Gif")]
public void JpegCore() public void GifCore()
{ {
using (MemoryStream memoryStream = new MemoryStream()) using (MemoryStream memoryStream = new MemoryStream())
{ {

4
tests/ImageSharp.Benchmarks/Image/EncodePng.cs

@ -32,7 +32,7 @@ namespace ImageSharp.Benchmarks.Image
} }
[Benchmark(Baseline = true, Description = "System.Drawing Png")] [Benchmark(Baseline = true, Description = "System.Drawing Png")]
public void JpegSystemDrawing() public void PngSystemDrawing()
{ {
using (MemoryStream memoryStream = new MemoryStream()) using (MemoryStream memoryStream = new MemoryStream())
{ {
@ -41,7 +41,7 @@ namespace ImageSharp.Benchmarks.Image
} }
[Benchmark(Description = "ImageSharp Png")] [Benchmark(Description = "ImageSharp Png")]
public void JpegCore() public void PngCore()
{ {
using (MemoryStream memoryStream = new MemoryStream()) using (MemoryStream memoryStream = new MemoryStream())
{ {

Loading…
Cancel
Save