Skip to content

Commit

Permalink
Use ArrayPool<T>
Browse files Browse the repository at this point in the history
  • Loading branch information
JimBobSquarePants committed Nov 9, 2016
1 parent c536079 commit bdb53d8
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 35 deletions.
9 changes: 6 additions & 3 deletions src/ImageSharp/Formats/Png/Filters/NoneFilter.cs
Expand Up @@ -5,6 +5,8 @@

namespace ImageSharp.Formats
{
using System;

/// <summary>
/// The None filter, the scanline is transmitted unmodified; it is only necessary to
/// insert a filter type byte before the data.
Expand All @@ -27,13 +29,14 @@ public static byte[] Decode(byte[] scanline)
/// Encodes the scanline
/// </summary>
/// <param name="scanline">The scanline to encode</param>
/// <param name="bytesPerScanline">The number of bytes per scanline</param>
/// <returns>The <see cref="T:byte[]"/></returns>
public static byte[] Encode(byte[] scanline)
public static byte[] Encode(byte[] scanline, int bytesPerScanline)
{
// Insert a byte before the data.
byte[] encodedScanline = new byte[scanline.Length + 1];
byte[] encodedScanline = new byte[bytesPerScanline + 1];
encodedScanline[0] = (byte)FilterType.None;
scanline.CopyTo(encodedScanline, 1);
Buffer.BlockCopy(scanline, 0, encodedScanline, 1, bytesPerScanline);

return encodedScanline;
}
Expand Down
73 changes: 42 additions & 31 deletions src/ImageSharp/Formats/Png/PngEncoderCore.cs
Expand Up @@ -6,10 +6,10 @@
namespace ImageSharp.Formats
{
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

using Quantizers;

Expand Down Expand Up @@ -125,7 +125,7 @@ internal sealed class PngEncoderCore
this.chunkDataBuffer[4] = 0x0D; // Line ending CRLF
this.chunkDataBuffer[5] = 0x0A; // Line ending CRLF
this.chunkDataBuffer[6] = 0x1A; // EOF
this.chunkDataBuffer[7] = 0x0A; // LF
this.chunkDataBuffer[7] = 0x0A; // LF

stream.Write(this.chunkDataBuffer, 0, 8);

Expand All @@ -139,6 +139,11 @@ internal sealed class PngEncoderCore
this.PngColorType = PngColorType.Palette;
}

if (this.PngColorType == PngColorType.Palette && this.Quality > 256)
{
this.Quality = 256;
}

// Set correct bit depth.
this.bitDepth = this.Quality <= 256
? (byte)ImageMaths.GetBitsNeededForColorDepth(this.Quality).Clamp(1, 8)
Expand Down Expand Up @@ -169,8 +174,7 @@ internal sealed class PngEncoderCore

this.WriteHeaderChunk(stream, header);

// Collect the pixel data
// TODO: Avoid doing this all at once and try row by row.
// Collect the indexed pixel data
if (this.PngColorType == PngColorType.Palette)
{
this.CollectIndexedBytes(image, stream, header);
Expand Down Expand Up @@ -235,8 +239,7 @@ private static void WriteInteger(Stream stream, uint value)
where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct
{
// Quatize the image and get the pixels.
// TODO: It might be an idea to add a pixel accessor to QuantizedImage to allow us to work by row.
// Quantize the image and get the pixels.
QuantizedImage<TColor, TPacked> quantized = this.WritePaletteChunk(stream, header, image);
this.palettePixelData = quantized.Pixels;
}
Expand All @@ -254,16 +257,16 @@ private static void WriteInteger(Stream stream, uint value)
where TPacked : struct
{
// Copy the pixels across from the image.
byte[] bytes = new byte[4];
// Reuse the chunk type buffer.
using (PixelAccessor<TColor, TPacked> pixels = image.Lock())
{
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(bytes, 0, ComponentOrder.XYZW);
byte luminance = (byte)((0.299F * bytes[0]) + (0.587F * bytes[1]) + (0.114F * bytes[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++)
{
Expand All @@ -273,7 +276,7 @@ private static void WriteInteger(Stream stream, uint value)
}
else
{
rawScanline[offset + i] = bytes[3];
rawScanline[offset + i] = this.chunkTypeBuffer[3];
}
}
}
Expand Down Expand Up @@ -355,7 +358,7 @@ private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousSca
{
candidates = new Tuple<byte[], int>[1];

byte[] none = NoneFilter.Encode(rawScanline);
byte[] none = NoneFilter.Encode(rawScanline, bytesPerScanline);
candidates[0] = new Tuple<byte[], int>(none, this.CalculateTotalVariation(none));
}
else
Expand Down Expand Up @@ -488,36 +491,44 @@ private void WriteHeaderChunk(Stream stream, PngHeader header)

// Get max colors for bit depth.
int colorTableLength = (int)Math.Pow(2, header.BitDepth) * 3;
byte[] colorTable = new byte[colorTableLength];

// TODO: Optimize this.
Parallel.For(
0,
pixelCount,
Bootstrapper.Instance.ParallelOptions,
i =>
byte[] colorTable = ArrayPool<byte>.Shared.Rent(colorTableLength);
byte[] bytes = ArrayPool<byte>.Shared.Rent(4);

try
{
for (int i = 0; i < pixelCount; i++)
{
int offset = i * 3;
Color color = new Color(palette[i].ToVector4());
int alpha = color.A;
palette[i].ToBytes(bytes, 0, ComponentOrder.XYZW);

int alpha = bytes[3];

// Premultiply the color. This helps prevent banding.
// TODO: Vector<byte>?
if (alpha < 255 && alpha > this.Threshold)
{
color = Color.Multiply(color, new Color(alpha, alpha, alpha, 255));
bytes[0] = (byte)(bytes[0] * alpha).Clamp(0, 255);
bytes[1] = (byte)(bytes[1] * alpha).Clamp(0, 255);
bytes[2] = (byte)(bytes[2] * alpha).Clamp(0, 255);
}

colorTable[offset] = color.R;
colorTable[offset + 1] = color.G;
colorTable[offset + 2] = color.B;
colorTable[offset] = bytes[0];
colorTable[offset + 1] = bytes[1];
colorTable[offset + 2] = bytes[2];

if (alpha <= this.Threshold)
{
transparentPixels.Add((byte)offset);
}
});
}

this.WriteChunk(stream, PngChunkTypes.Palette, colorTable);
this.WriteChunk(stream, PngChunkTypes.Palette, colorTable, 0, colorTableLength);
}
finally
{
ArrayPool<byte>.Shared.Return(colorTable);
ArrayPool<byte>.Shared.Return(bytes);
}

// Write the transparency data
if (transparentPixels.Any())
Expand Down Expand Up @@ -588,10 +599,8 @@ private void WriteGammaChunk(Stream stream)
where TPacked : struct
{
int bytesPerScanline = this.width * this.bytesPerPixel;

// TODO: These could be rented
byte[] previousScanline = new byte[bytesPerScanline];
byte[] rawScanline = new byte[bytesPerScanline];
byte[] previousScanline = ArrayPool<byte>.Shared.Rent(bytesPerScanline);
byte[] rawScanline = ArrayPool<byte>.Shared.Rent(bytesPerScanline);

byte[] buffer;
int bufferLength;
Expand Down Expand Up @@ -619,6 +628,8 @@ private void WriteGammaChunk(Stream stream)
}
finally
{
ArrayPool<byte>.Shared.Return(previousScanline);
ArrayPool<byte>.Shared.Return(rawScanline);
memoryStream?.Dispose();
}

Expand Down
6 changes: 5 additions & 1 deletion src/ImageSharp/project.json
Expand Up @@ -45,7 +45,11 @@
"System.Threading": "4.0.11",
"System.Threading.Tasks": "4.0.11",
"System.Threading.Tasks.Parallel": "4.0.1",
"StyleCop.Analyzers": { "version": "1.0.0", "type": "build" }
"StyleCop.Analyzers": {
"version": "1.0.0",
"type": "build"
},
"System.Buffers": "4.0.0"
},
"frameworks": {
"netstandard1.1": {}
Expand Down

0 comments on commit bdb53d8

Please sign in to comment.