diff --git a/src/ImageSharp/Formats/Jpg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpg/JpegEncoder.cs
index 7b3ffe5b35..0fc1f3d353 100644
--- a/src/ImageSharp/Formats/Jpg/JpegEncoder.cs
+++ b/src/ImageSharp/Formats/Jpg/JpegEncoder.cs
@@ -48,7 +48,11 @@ namespace ImageSharp.Formats
/// The subsample ratio of the jpg image.
public JpegSubsample Subsample
{
- get { return this.subsample; }
+ get
+ {
+ return this.subsample;
+ }
+
set
{
this.subsample = value;
diff --git a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
index e1eab40151..c9ffd855b5 100644
--- a/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpg/JpegEncoderCore.cs
@@ -11,7 +11,7 @@ namespace ImageSharp.Formats
///
/// Image encoder for writing an image to a stream as a jpeg.
///
- internal class JpegEncoderCore
+ internal unsafe class JpegEncoderCore
{
///
/// 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
///
private readonly byte[] buffer = new byte[16];
+ ///
+ /// A buffer for reducing the number of stream writes when emitting Huffman tables. 64 seems to be enough.
+ ///
+ private readonly byte[] emitBuffer = new byte[64];
+
+ ///
+ /// A buffer for reducing the number of stream writes when emitting Huffman tables. Max combined table lengths + identifier.
+ ///
+ private readonly byte[] huffmanBuffer = new byte[179];
+
///
/// The scaled quantization tables, in zig-zag order.
///
@@ -385,17 +395,6 @@ namespace ImageSharp.Formats
return -((-dividend + (divisor >> 1)) / divisor);
}
- ///
- /// Writes the given byte to the stream.
- ///
- /// The byte to write.
- private void WriteByte(byte b)
- {
- byte[] data = new byte[1];
- data[0] = b;
- this.outputStream.Write(data, 0, 1);
- }
-
///
/// Emits the least significant count of bits of bits to the bit-stream.
/// The precondition is bits < 1<<nBits && nBits <= 16.
@@ -404,21 +403,32 @@ namespace ImageSharp.Formats
/// The number of bits
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);
}
}
diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs
index 2b53d02538..b9a226fc8c 100644
--- a/tests/ImageSharp.Tests/FileTestBase.cs
+++ b/tests/ImageSharp.Tests/FileTestBase.cs
@@ -5,8 +5,8 @@
namespace ImageSharp.Tests
{
- using System.IO;
using System.Collections.Generic;
+ using System.IO;
///
/// The test base class for reading and writing to files.
@@ -18,22 +18,22 @@ namespace ImageSharp.Tests
///
protected static readonly List Files = new List
{
- //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;