diff --git a/src/ImageSharp/Common/Extensions/ListExtensions.cs b/src/ImageSharp/Common/Extensions/ListExtensions.cs
new file mode 100644
index 000000000..752f7ef21
--- /dev/null
+++ b/src/ImageSharp/Common/Extensions/ListExtensions.cs
@@ -0,0 +1,48 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Common.Extensions
+{
+ using System.Collections.Generic;
+
+ ///
+ /// Encapsulates a series of time saving extension methods to the class.
+ ///
+ internal static class ListExtensions
+ {
+ ///
+ /// Inserts an item at the given index automatically expanding the capacity if required.
+ ///
+ /// The type of object within the list
+ /// The list
+ /// The index
+ /// The item to insert
+ public static void SafeInsert(this List list, int index, T item)
+ {
+ if (index >= list.Count)
+ {
+ list.Add(item);
+ }
+ else
+ {
+ list[index] = item;
+ }
+ }
+
+ ///
+ /// Removes the last element from a list and returns that element. This method changes the length of the list.
+ ///
+ /// The type of object within the list
+ /// The list
+ /// The last element in the specified sequence.
+ public static T Pop(this List list)
+ {
+ int last = list.Count - 1;
+ T item = list[last];
+ list.RemoveAt(last);
+ return item;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Port/Components/HuffmanBranch.cs b/src/ImageSharp/Formats/Jpeg/Port/Components/HuffmanBranch.cs
new file mode 100644
index 000000000..0f0a9b540
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Port/Components/HuffmanBranch.cs
@@ -0,0 +1,55 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats.Jpeg.Port.Components
+{
+ using System.Collections.Generic;
+
+ ///
+ /// Represents a branch in the huffman tree
+ ///
+ internal struct HuffmanBranch
+ {
+ ///
+ /// The index
+ ///
+ public int Index;
+
+ ///
+ /// The value
+ ///
+ public short Value;
+
+ ///
+ /// The children
+ ///
+ public List Children;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The value
+ public HuffmanBranch(short value)
+ : this(value, new List())
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The branch children
+ public HuffmanBranch(List children)
+ : this((short)0, children)
+ {
+ }
+
+ private HuffmanBranch(short value, List children)
+ {
+ this.Index = 0;
+ this.Value = value;
+ this.Children = children;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Port/Components/HuffmanTables.cs b/src/ImageSharp/Formats/Jpeg/Port/Components/HuffmanTables.cs
index 5fcc2007b..08c37bcbc 100644
--- a/src/ImageSharp/Formats/Jpeg/Port/Components/HuffmanTables.cs
+++ b/src/ImageSharp/Formats/Jpeg/Port/Components/HuffmanTables.cs
@@ -1,43 +1,50 @@
-namespace ImageSharp.Formats.Jpeg.Port.Components
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats.Jpeg.Port.Components
{
using System.Collections.Generic;
-
- using ImageSharp.Memory;
+ using System.Runtime.CompilerServices;
///
/// Defines a pair of huffman tables
///
internal class HuffmanTables
{
- ///
- /// Gets or sets the quantization tables.
- ///
- public Fast2DArray Tables { get; set; } = new Fast2DArray(256, 2);
- }
-
- internal struct HuffmanBranch
- {
- public HuffmanBranch(short value)
- : this(value, new List())
- {
- }
+ private List first = new List();
- public HuffmanBranch(List children)
- : this(0, children)
- {
- }
+ private List second = new List();
- private HuffmanBranch(short value, List children)
+ ///
+ /// Gets or sets the table at the given index.
+ ///
+ /// The index
+ /// The
+ public List this[int index]
{
- this.Index = 0;
- this.Value = value;
- this.Children = children;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ if (index == 0)
+ {
+ return this.first;
+ }
+
+ return this.second;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ if (index == 0)
+ {
+ this.first = value;
+ }
+
+ this.second = value;
+ }
}
-
- public int Index;
-
- public short Value;
-
- public List Children;
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/Port/Huffman.cs b/src/ImageSharp/Formats/Jpeg/Port/Huffman.cs
deleted file mode 100644
index 75b6dc562..000000000
--- a/src/ImageSharp/Formats/Jpeg/Port/Huffman.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace ImageSharp.Formats.Jpeg.Port
-{
- class Huffman
- {
- }
-}
diff --git a/src/ImageSharp/Formats/Jpeg/Port/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/Port/JpegDecoderCore.cs
index db25ba845..95a83b086 100644
--- a/src/ImageSharp/Formats/Jpeg/Port/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/Port/JpegDecoderCore.cs
@@ -8,8 +8,8 @@ namespace ImageSharp.Formats.Jpeg.Port
using System;
using System.Collections.Generic;
using System.IO;
- using System.Linq;
+ using ImageSharp.Common.Extensions;
using ImageSharp.Formats.Jpeg.Port.Components;
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
@@ -50,11 +50,6 @@ namespace ImageSharp.Formats.Jpeg.Port
///
private JFif jFif;
- ///
- /// Whether the image has a EXIF header
- ///
- private bool isExif;
-
///
/// Initializes a new instance of the class.
///
@@ -345,108 +340,96 @@ namespace ImageSharp.Formats.Jpeg.Port
throw new ImageFormatException("DHT has wrong length");
}
- using (var huffmanData = new Buffer(remaining))
+ using (var huffmanData = new Buffer(16))
{
- this.InputStream.Skip(1);
- this.InputStream.Read(huffmanData.Array, 0, remaining);
-
- for (int i = 0; i < remaining;)
+ for (int i = 2; i < remaining;)
{
- byte huffmanTableSpec = huffmanData[i];
- byte[] codeLengths = new byte[16];
- int codeLengthSum = 0;
-
- for (int j = 0; j < 16; j++)
- {
- codeLengthSum += codeLengths[j] = huffmanData[j];
- }
+ byte huffmanTableSpec = (byte)this.InputStream.ReadByte();
+ this.InputStream.Read(huffmanData.Array, 0, 16);
- // TODO: Pooling?
- short[] huffmanValues = new short[codeLengthSum];
- using (var values = new Buffer(codeLengthSum))
+ using (var codeLengths = new Buffer(16))
{
- this.InputStream.Read(values.Array, 0, codeLengthSum);
+ int codeLengthSum = 0;
- for (int j = 0; j < codeLengthSum; j++)
+ for (int j = 0; j < 16; j++)
{
- huffmanValues[j] = values[j];
+ codeLengthSum += codeLengths[j] = huffmanData[j];
}
- i += 17 + codeLengthSum;
+ using (var huffmanValues = new Buffer(codeLengthSum))
+ {
+ this.InputStream.Read(huffmanValues.Array, 0, codeLengthSum);
+
+ i += 17 + codeLengthSum;
- this.BuildHuffmanTable(
- huffmanTableSpec >> 4 == 0 ? this.dcHuffmanTables : this.acHuffmanTables,
- huffmanTableSpec & 15,
- codeLengths,
- huffmanValues);
+ // Everything I can discover indicates there's a max of two table per DC AC pair though this limits the index to 16?
+ this.BuildHuffmanTable(
+ huffmanTableSpec >> 4 == 0 ? this.dcHuffmanTables : this.acHuffmanTables,
+ huffmanTableSpec & 15,
+ codeLengths.Array,
+ huffmanValues.Array);
+ }
}
}
}
}
- private void BuildHuffmanTable(HuffmanTables tables, int index, byte[] codeLengths, short[] values)
+ ///
+ /// Builds the huffman tables
+ ///
+ /// The tables
+ /// The table index
+ /// The codelengths
+ /// The values
+ private void BuildHuffmanTable(HuffmanTables tables, int index, byte[] codeLengths, byte[] values)
{
- // (╯°□°)╯︵ ┻━┻ Everything up to here is going well. I can't match the JavaScript now though.
int length = 16;
while (length > 0 && codeLengths[length - 1] == 0)
{
length--;
}
- var code = new Queue();
- code.Enqueue(new HuffmanBranch(new List()));
- HuffmanBranch p = code.Peek();
- p.Children = new List();
- HuffmanBranch q;
+ // TODO: Check the capacity here. Seems to max at 2
+ var code = new List { new HuffmanBranch(new List()) };
+ HuffmanBranch p = code[0];
int k = 0;
- try
+
+ for (int i = 0; i < length; i++)
{
- for (int i = 0; i < length; i++)
+ HuffmanBranch q;
+ for (int j = 0; j < codeLengths[i]; j++)
{
- for (int j = 0; j < codeLengths[i]; j++)
+ p = code.Pop();
+ p.Children.SafeInsert(p.Index, new HuffmanBranch(values[k]));
+ while (p.Index > 0)
{
- p = code.Dequeue();
- p.Children.Add(new HuffmanBranch(values[k]));
- while (p.Index > 0)
- {
- p = code.Dequeue();
- }
-
- p.Index++;
- code.Enqueue(p);
- while (code.Count <= i)
- {
- q = new HuffmanBranch(new List());
- code.Enqueue(q);
- p.Children.Add(new HuffmanBranch(q.Children));
- p = q;
- }
-
- k++;
+ p = code.Pop();
}
- if (i + 1 < length)
+ p.Index++;
+ code.Add(p);
+ while (code.Count <= i)
{
- // p here points to last code
q = new HuffmanBranch(new List());
- code.Enqueue(q);
- p.Children.Add(new HuffmanBranch(q.Children));
+ code.Add(q);
+ p.Children.SafeInsert(p.Index, new HuffmanBranch(q.Children));
p = q;
}
- }
- Span tableSpan = tables.Tables.GetRowSpan(index);
+ k++;
+ }
- List result = code.Peek().Children;
- for (int i = 0; i < result.Count; i++)
+ if (i + 1 < length)
{
- tableSpan[i] = result[i];
+ // p here points to last code
+ q = new HuffmanBranch(new List());
+ code.Add(q);
+ p.Children.SafeInsert(p.Index, new HuffmanBranch(q.Children));
+ p = q;
}
}
- catch (Exception e)
- {
- throw;
- }
+
+ tables[index] = code[0].Children;
}
///
@@ -533,8 +516,7 @@ namespace ImageSharp.Formats.Jpeg.Port
private ushort ReadUint16()
{
this.InputStream.Read(this.uint16Buffer, 0, 2);
- ushort value = (ushort)((this.uint16Buffer[0] << 8) | this.uint16Buffer[1]);
- return value;
+ return (ushort)((this.uint16Buffer[0] << 8) | this.uint16Buffer[1]);
}
///
diff --git a/src/ImageSharp/Formats/Jpeg/Port/JpegFrame.cs b/src/ImageSharp/Formats/Jpeg/Port/JpegFrame.cs
deleted file mode 100644
index a279339e7..000000000
--- a/src/ImageSharp/Formats/Jpeg/Port/JpegFrame.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-namespace ImageSharp.Formats.Jpeg.Port
-{
- ///
- /// Represents a jpeg frame
- ///
- internal class JpegFrame
- {
- ///
- /// Gets or sets a value indicating whether the fame is extended
- ///
- public bool Extended { get; set; }
-
- ///
- /// Gets or sets a value indicating whether the fame is progressive
- ///
- public bool Progressive { get; set; }
-
- ///
- /// Gets or sets the precision
- ///
- public byte Precision { get; set; }
-
- ///
- /// Gets or sets the number of scanlines within the frame
- ///
- public short Scanlines { get; set; }
-
- ///
- /// Gets or sets the number of samples per scanline
- ///
- public short SamplesPerLine { get; set; }
- }
-}