Browse Source

Make PngEncoder threadsafe. Fix #24

pull/30/head
James Jackson-South 10 years ago
parent
commit
8cae170251
  1. 99
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  2. 19
      tests/ImageSharp.Tests/Formats/Png/PngTests.cs

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

@ -60,6 +60,26 @@ namespace ImageSharp.Formats
/// </summary>
private int bytesPerPixel;
/// <summary>
/// The buffer for the sub filter
/// </summary>
private byte[] sub;
/// <summary>
/// The buffer for the up filter
/// </summary>
private byte[] up;
/// <summary>
/// The buffer for the average filter
/// </summary>
private byte[] average;
/// <summary>
/// The buffer for the paeth filter
/// </summary>
private byte[] paeth;
/// <summary>
/// Gets or sets the quality of output for images.
/// </summary>
@ -356,54 +376,39 @@ namespace ImageSharp.Formats
return result;
}
byte[] sub = ArrayPool<byte>.Shared.Rent(bytesPerScanline + 1);
byte[] up = ArrayPool<byte>.Shared.Rent(bytesPerScanline + 1);
byte[] average = ArrayPool<byte>.Shared.Rent(bytesPerScanline + 1);
byte[] paeth = ArrayPool<byte>.Shared.Rent(bytesPerScanline + 1);
SubFilter.Encode(rawScanline, this.sub, this.bytesPerPixel, bytesPerScanline);
int currentTotalVariation = this.CalculateTotalVariation(this.sub, bytesPerScanline);
int lowestTotalVariation = currentTotalVariation;
try
{
SubFilter.Encode(rawScanline, sub, this.bytesPerPixel, bytesPerScanline);
int currentTotalVariation = this.CalculateTotalVariation(sub, bytesPerScanline);
int lowestTotalVariation = currentTotalVariation;
result = this.sub;
result = sub;
UpFilter.Encode(rawScanline, previousScanline, up, bytesPerScanline);
currentTotalVariation = this.CalculateTotalVariation(up, bytesPerScanline);
if (currentTotalVariation < lowestTotalVariation)
{
lowestTotalVariation = currentTotalVariation;
result = up;
}
UpFilter.Encode(rawScanline, previousScanline, this.up, bytesPerScanline);
currentTotalVariation = this.CalculateTotalVariation(this.up, bytesPerScanline);
AverageFilter.Encode(rawScanline, previousScanline, average, this.bytesPerPixel, bytesPerScanline);
currentTotalVariation = this.CalculateTotalVariation(average, bytesPerScanline);
if (currentTotalVariation < lowestTotalVariation)
{
lowestTotalVariation = currentTotalVariation;
result = this.up;
}
if (currentTotalVariation < lowestTotalVariation)
{
lowestTotalVariation = currentTotalVariation;
result = average;
}
AverageFilter.Encode(rawScanline, previousScanline, this.average, this.bytesPerPixel, bytesPerScanline);
currentTotalVariation = this.CalculateTotalVariation(this.average, bytesPerScanline);
PaethFilter.Encode(rawScanline, previousScanline, paeth, this.bytesPerPixel, bytesPerScanline);
currentTotalVariation = this.CalculateTotalVariation(paeth, bytesPerScanline);
if (currentTotalVariation < lowestTotalVariation)
{
lowestTotalVariation = currentTotalVariation;
result = this.average;
}
if (currentTotalVariation < lowestTotalVariation)
{
result = paeth;
}
PaethFilter.Encode(rawScanline, previousScanline, this.paeth, this.bytesPerPixel, bytesPerScanline);
currentTotalVariation = this.CalculateTotalVariation(this.paeth, bytesPerScanline);
return result;
}
finally
if (currentTotalVariation < lowestTotalVariation)
{
ArrayPool<byte>.Shared.Return(sub);
ArrayPool<byte>.Shared.Return(up);
ArrayPool<byte>.Shared.Return(average);
ArrayPool<byte>.Shared.Return(paeth);
result = this.paeth;
}
return result;
}
/// <summary>
@ -618,6 +623,14 @@ namespace ImageSharp.Formats
int resultLength = bytesPerScanline + 1;
byte[] result = ArrayPool<byte>.Shared.Rent(resultLength);
if (this.PngColorType != PngColorType.Palette)
{
this.sub = ArrayPool<byte>.Shared.Rent(resultLength);
this.up = ArrayPool<byte>.Shared.Rent(resultLength);
this.average = ArrayPool<byte>.Shared.Rent(resultLength);
this.paeth = ArrayPool<byte>.Shared.Rent(resultLength);
}
byte[] buffer;
int bufferLength;
MemoryStream memoryStream = null;
@ -643,6 +656,14 @@ namespace ImageSharp.Formats
ArrayPool<byte>.Shared.Return(previousScanline);
ArrayPool<byte>.Shared.Return(rawScanline);
ArrayPool<byte>.Shared.Return(result);
if (this.PngColorType != PngColorType.Palette)
{
ArrayPool<byte>.Shared.Return(this.sub);
ArrayPool<byte>.Shared.Return(this.up);
ArrayPool<byte>.Shared.Return(this.average);
ArrayPool<byte>.Shared.Return(this.paeth);
}
}
// Store the chunks in repeated 64k blocks.

19
tests/ImageSharp.Tests/Formats/Png/PngTests.cs

@ -6,6 +6,7 @@
namespace ImageSharp.Tests
{
using System.IO;
using System.Threading.Tasks;
using Formats;
@ -29,5 +30,23 @@ namespace ImageSharp.Tests
}
}
}
[Fact]
public void ImageCanSavePngInParallel()
{
string path = this.CreateOutputDirectory("Png");
Parallel.ForEach(
Files,
file =>
{
Image image = file.CreateImage();
using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.png"))
{
image.Save(output, new PngFormat());
}
});
}
}
}
Loading…
Cancel
Save