Browse Source

Compress image data line by line.

pull/23/head
James Jackson-South 9 years ago
parent
commit
52cddd328e
  1. 1
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  2. 65
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  3. 4
      src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs

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

@ -2,6 +2,7 @@
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using System;

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

@ -174,6 +174,7 @@ namespace ImageSharp.Formats
this.WriteHeaderChunk(stream, header);
// Collect the pixel data
// TODO: Avoid doing this all at once and try row by row.
if (this.PngColorType == PngColorType.Palette)
{
this.CollectIndexedBytes(image, stream, header);
@ -328,38 +329,17 @@ namespace ImageSharp.Formats
/// Encodes the pixel data line by line.
/// Each scanline is encoded in the most optimal manner to improve compression.
/// </summary>
/// <param name="row">The row.</param>
/// <param name="previousScanline">The previous scanline.</param>
/// <param name="rawScanline">The raw scanline.</param>
/// <param name="bytesPerScanline">The number of bytes per scanline.</param>
/// <returns>The <see cref="T:byte[]"/></returns>
private byte[] EncodePixelData()
private byte[] EncodePixelRow(int row, byte[] previousScanline, byte[] rawScanline, int bytesPerScanline)
{
byte[][] filteredScanlines = new byte[this.height][];
int bytesPerScanline = this.width * this.bytesPerPixel;
int length = 0;
byte[] previousScanline = new byte[bytesPerScanline];
byte[] rawScanline = new byte[bytesPerScanline];
for (int y = 0; y < this.height; y++)
{
Buffer.BlockCopy(this.pixelData, y * bytesPerScanline, rawScanline, 0, bytesPerScanline);
byte[] filteredScanline = this.GetOptimalFilteredScanline(rawScanline, previousScanline, bytesPerScanline, this.bytesPerPixel);
length += filteredScanline.Length;
filteredScanlines[y] = filteredScanline;
// Do a bit of shuffling;
byte[] tmp = rawScanline;
rawScanline = previousScanline;
previousScanline = tmp;
}
// Flatten the jagged array
byte[] result = new byte[length];
for (int i = 0; i < this.height; i++)
{
int len = filteredScanlines[i].Length;
Buffer.BlockCopy(filteredScanlines[i], 0, result, i * len, len);
}
Buffer.BlockCopy(this.pixelData, row * bytesPerScanline, rawScanline, 0, bytesPerScanline);
byte[] filteredScanline = this.GetOptimalFilteredScanline(rawScanline, previousScanline, bytesPerScanline, this.bytesPerPixel);
return result;
return filteredScanline;
}
/// <summary>
@ -590,28 +570,39 @@ namespace ImageSharp.Formats
/// <summary>
/// Writes the pixel information to the stream.
/// TODO: This is WHACK! We should be able to do this without creating yet another array.
/// </summary>
/// <param name="stream">The stream.</param>
private void WriteDataChunks(Stream stream)
{
byte[] data = this.EncodePixelData();
int bytesPerScanline = this.width * this.bytesPerPixel;
// TODO: These could be rented
byte[] previousScanline = new byte[bytesPerScanline];
byte[] rawScanline = new byte[bytesPerScanline];
byte[] buffer;
int bufferLength;
MemoryStream memoryStream = null;
try
{
memoryStream = new MemoryStream();
using (ZlibDeflateStream deflateStream = new ZlibDeflateStream(memoryStream, this.CompressionLevel))
{
deflateStream.Write(data, 0, data.Length);
}
for (int y = 0; y < this.height; y++)
{
byte[] data = this.EncodePixelRow(y, previousScanline, rawScanline, bytesPerScanline);
deflateStream.Write(data, 0, data.Length);
deflateStream.Flush();
// Do a bit of shuffling;
byte[] tmp = rawScanline;
rawScanline = previousScanline;
previousScanline = tmp;
}
bufferLength = (int)memoryStream.Length;
buffer = memoryStream.ToArray();
bufferLength = (int)memoryStream.Length;
buffer = memoryStream.ToArray();
}
}
finally
{

4
src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs

@ -37,7 +37,9 @@ namespace ImageSharp.Formats
/// </remarks>
private bool isDisposed;
// The stream responsible for decompressing the input stream.
/// <summary>
/// The stream responsible for compressing the input stream.
/// </summary>
private DeflateStream deflateStream;
/// <summary>

Loading…
Cancel
Save