Browse Source

Make PngEncoder threadsafe. Fix #24

af/merge-core
James Jackson-South 10 years ago
parent
commit
308e6f0607
  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> /// </summary>
private int bytesPerPixel; 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> /// <summary>
/// Gets or sets the quality of output for images. /// Gets or sets the quality of output for images.
/// </summary> /// </summary>
@ -356,54 +376,39 @@ namespace ImageSharp.Formats
return result; return result;
} }
byte[] sub = ArrayPool<byte>.Shared.Rent(bytesPerScanline + 1); SubFilter.Encode(rawScanline, this.sub, this.bytesPerPixel, bytesPerScanline);
byte[] up = ArrayPool<byte>.Shared.Rent(bytesPerScanline + 1); int currentTotalVariation = this.CalculateTotalVariation(this.sub, bytesPerScanline);
byte[] average = ArrayPool<byte>.Shared.Rent(bytesPerScanline + 1); int lowestTotalVariation = currentTotalVariation;
byte[] paeth = ArrayPool<byte>.Shared.Rent(bytesPerScanline + 1);
try result = this.sub;
{
SubFilter.Encode(rawScanline, sub, this.bytesPerPixel, bytesPerScanline);
int currentTotalVariation = this.CalculateTotalVariation(sub, bytesPerScanline);
int lowestTotalVariation = currentTotalVariation;
result = sub; UpFilter.Encode(rawScanline, previousScanline, this.up, bytesPerScanline);
currentTotalVariation = this.CalculateTotalVariation(this.up, bytesPerScanline);
UpFilter.Encode(rawScanline, previousScanline, up, bytesPerScanline);
currentTotalVariation = this.CalculateTotalVariation(up, bytesPerScanline);
if (currentTotalVariation < lowestTotalVariation)
{
lowestTotalVariation = currentTotalVariation;
result = up;
}
AverageFilter.Encode(rawScanline, previousScanline, average, this.bytesPerPixel, bytesPerScanline); if (currentTotalVariation < lowestTotalVariation)
currentTotalVariation = this.CalculateTotalVariation(average, bytesPerScanline); {
lowestTotalVariation = currentTotalVariation;
result = this.up;
}
if (currentTotalVariation < lowestTotalVariation) AverageFilter.Encode(rawScanline, previousScanline, this.average, this.bytesPerPixel, bytesPerScanline);
{ currentTotalVariation = this.CalculateTotalVariation(this.average, bytesPerScanline);
lowestTotalVariation = currentTotalVariation;
result = average;
}
PaethFilter.Encode(rawScanline, previousScanline, paeth, this.bytesPerPixel, bytesPerScanline); if (currentTotalVariation < lowestTotalVariation)
currentTotalVariation = this.CalculateTotalVariation(paeth, bytesPerScanline); {
lowestTotalVariation = currentTotalVariation;
result = this.average;
}
if (currentTotalVariation < lowestTotalVariation) PaethFilter.Encode(rawScanline, previousScanline, this.paeth, this.bytesPerPixel, bytesPerScanline);
{ currentTotalVariation = this.CalculateTotalVariation(this.paeth, bytesPerScanline);
result = paeth;
}
return result; if (currentTotalVariation < lowestTotalVariation)
}
finally
{ {
ArrayPool<byte>.Shared.Return(sub); result = this.paeth;
ArrayPool<byte>.Shared.Return(up);
ArrayPool<byte>.Shared.Return(average);
ArrayPool<byte>.Shared.Return(paeth);
} }
return result;
} }
/// <summary> /// <summary>
@ -618,6 +623,14 @@ namespace ImageSharp.Formats
int resultLength = bytesPerScanline + 1; int resultLength = bytesPerScanline + 1;
byte[] result = ArrayPool<byte>.Shared.Rent(resultLength); 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; byte[] buffer;
int bufferLength; int bufferLength;
MemoryStream memoryStream = null; MemoryStream memoryStream = null;
@ -643,6 +656,14 @@ namespace ImageSharp.Formats
ArrayPool<byte>.Shared.Return(previousScanline); ArrayPool<byte>.Shared.Return(previousScanline);
ArrayPool<byte>.Shared.Return(rawScanline); ArrayPool<byte>.Shared.Return(rawScanline);
ArrayPool<byte>.Shared.Return(result); 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. // Store the chunks in repeated 64k blocks.

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

@ -6,6 +6,7 @@
namespace ImageSharp.Tests namespace ImageSharp.Tests
{ {
using System.IO; using System.IO;
using System.Threading.Tasks;
using Formats; 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