Browse Source

Reduce allocations when encoding

Down to 2.8X Sys.Draw with 1/4 memory per allocation. No tsure where to
go from here. Vectors maybe?
pull/23/head
James Jackson-South 9 years ago
parent
commit
7b09a06b68
  1. 6
      src/ImageSharp/Formats/Jpg/JpegEncoder.cs
  2. 80
      src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
  3. 26
      tests/ImageSharp.Tests/FileTestBase.cs

6
src/ImageSharp/Formats/Jpg/JpegEncoder.cs

@ -48,7 +48,11 @@ namespace ImageSharp.Formats
/// <value>The subsample ratio of the jpg image.</value>
public JpegSubsample Subsample
{
get { return this.subsample; }
get
{
return this.subsample;
}
set
{
this.subsample = value;

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

@ -11,7 +11,7 @@ namespace ImageSharp.Formats
/// <summary>
/// Image encoder for writing an image to a stream as a jpeg.
/// </summary>
internal class JpegEncoderCore
internal unsafe class JpegEncoderCore
{
/// <summary>
/// The number of quantization tables.
@ -102,7 +102,7 @@ namespace ImageSharp.Formats
0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa,
0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa
})
};
@ -159,6 +159,16 @@ namespace ImageSharp.Formats
/// </summary>
private readonly byte[] buffer = new byte[16];
/// <summary>
/// A buffer for reducing the number of stream writes when emitting Huffman tables. 64 seems to be enough.
/// </summary>
private readonly byte[] emitBuffer = new byte[64];
/// <summary>
/// A buffer for reducing the number of stream writes when emitting Huffman tables. Max combined table lengths + identifier.
/// </summary>
private readonly byte[] huffmanBuffer = new byte[179];
/// <summary>
/// The scaled quantization tables, in zig-zag order.
/// </summary>
@ -385,17 +395,6 @@ namespace ImageSharp.Formats
return -((-dividend + (divisor >> 1)) / divisor);
}
/// <summary>
/// Writes the given byte to the stream.
/// </summary>
/// <param name="b">The byte to write.</param>
private void WriteByte(byte b)
{
byte[] data = new byte[1];
data[0] = b;
this.outputStream.Write(data, 0, 1);
}
/// <summary>
/// Emits the least significant count of bits of bits to the bit-stream.
/// The precondition is bits <example>&lt; 1&lt;&lt;nBits &amp;&amp; nBits &lt;= 16</example>.
@ -404,21 +403,32 @@ namespace ImageSharp.Formats
/// <param name="count">The number of bits</param>
private void Emit(uint bits, uint count)
{
// TODO: This requires optimization. We have far too many writes to the underlying stream going on.
count += this.bitCount;
bits <<= (int)(32 - count);
bits |= this.accumulatedBits;
while (count >= 8)
// Only write if more than 8 bits.
if (count >= 8)
{
byte b = (byte)(bits >> 24);
this.WriteByte(b);
if (b == 0xff)
// Track length
int len = 0;
while (count >= 8)
{
this.WriteByte(0x00);
byte b = (byte)(bits >> 24);
this.emitBuffer[len++] = b;
if (b == 0xff)
{
this.emitBuffer[len++] = 0x00;
}
bits <<= 8;
count -= 8;
}
bits <<= 8;
count -= 8;
if (len > 0)
{
this.outputStream.Write(this.emitBuffer, 0, len);
}
}
this.accumulatedBits = bits;
@ -553,7 +563,7 @@ namespace ImageSharp.Formats
byte yy = (byte)((0.299F * r) + (0.587F * g) + (0.114F * b));
byte cb = (byte)(128 + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b)));
byte cr = (byte)(128 + ((0.5F * r) - (0.418688F * g) - (0.081312F * b)));
int index = (8 * j) + i;
yBlock[index] = yy;
cbBlock[index] = cb;
@ -776,11 +786,29 @@ namespace ImageSharp.Formats
for (int i = 0; i < specs.Length; i++)
{
HuffmanSpec spec = specs[i];
int len = 0;
fixed (byte* huffman = this.huffmanBuffer)
{
fixed (byte* count = spec.Count)
{
fixed (byte* values = spec.Values)
{
huffman[len++] = headers[i];
for (int c = 0; c < spec.Count.Length; c++)
{
huffman[len++] = count[c];
}
for (int v = 0; v < spec.Values.Length; v++)
{
huffman[len++] = values[v];
}
}
}
}
// TODO: Investigate optimizing this. It might be better to create a single array.
this.WriteByte(headers[i]);
this.outputStream.Write(spec.Count, 0, spec.Count.Length);
this.outputStream.Write(spec.Values, 0, spec.Values.Length);
this.outputStream.Write(this.huffmanBuffer, 0, len);
}
}

26
tests/ImageSharp.Tests/FileTestBase.cs

@ -5,8 +5,8 @@
namespace ImageSharp.Tests
{
using System.IO;
using System.Collections.Generic;
using System.IO;
/// <summary>
/// The test base class for reading and writing to files.
@ -18,22 +18,22 @@ namespace ImageSharp.Tests
/// </summary>
protected static readonly List<TestFile> Files = new List<TestFile>
{
//new TestFile(TestImages.Png.P1),
//new TestFile(TestImages.Png.Pd),
new TestFile(TestImages.Jpeg.Floorplan), // Perf: Enable for local testing only
// new TestFile(TestImages.Png.P1), // Perf: Enable for local testing only
// new TestFile(TestImages.Png.Pd), // Perf: Enable for local testing only
// new TestFile(TestImages.Jpeg.Floorplan), // Perf: Enable for local testing only
new TestFile(TestImages.Jpeg.Calliphora),
new TestFile(TestImages.Jpeg.Cmyk), // Perf: Enable for local testing only
// new TestFile(TestImages.Jpeg.Cmyk), // Perf: Enable for local testing only
new TestFile(TestImages.Jpeg.Turtle),
//new TestFile(TestImages.Jpeg.Fb), // Perf: Enable for local testing only
//new TestFile(TestImages.Jpeg.Progress), // Perf: Enable for local testing only
//new TestFile(TestImages.Jpeg.Gamma_dalai_lama_gray). // Perf: Enable for local testing only
// new TestFile(TestImages.Jpeg.Fb), // Perf: Enable for local testing only
// new TestFile(TestImages.Jpeg.Progress), // Perf: Enable for local testing only
// new TestFile(TestImages.Jpeg.GammaDalaiLamaGray), // Perf: Enable for local testing only
new TestFile(TestImages.Bmp.Car),
//new TestFile(TestImages.Bmp.Neg_height), // Perf: Enable for local testing only
//new TestFile(TestImages.Png.Blur), // Perf: Enable for local testing only
//new TestFile(TestImages.Png.Indexed), // Perf: Enable for local testing only
// new TestFile(TestImages.Bmp.Neg_height), // Perf: Enable for local testing only
// new TestFile(TestImages.Png.Blur), // Perf: Enable for local testing only
// new TestFile(TestImages.Png.Indexed), // Perf: Enable for local testing only
new TestFile(TestImages.Png.Splash),
new TestFile(TestImages.Gif.Rings),
//new TestFile(TestImages.Gif.Giphy) // Perf: Enable for local testing only
// new TestFile(TestImages.Gif.Giphy) // Perf: Enable for local testing only
};
protected string CreateOutputDirectory(string path)
@ -42,7 +42,7 @@ namespace ImageSharp.Tests
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
Directory.CreateDirectory(path);
}
return path;

Loading…
Cancel
Save