Skip to content

Commit

Permalink
(╯°□°)╯︵ ┻━┻
Browse files Browse the repository at this point in the history
  • Loading branch information
JimBobSquarePants committed Jun 19, 2017
1 parent b025ed6 commit 3728b82
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 15 deletions.
30 changes: 29 additions & 1 deletion src/ImageSharp/Formats/Jpeg/Port/Components/HuffmanTables.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
namespace ImageSharp.Formats.Jpeg.Port.Components
{
using System.Collections.Generic;

using ImageSharp.Memory;

/// <summary>
Expand All @@ -10,6 +12,32 @@ internal class HuffmanTables
/// <summary>
/// Gets or sets the quantization tables.
/// </summary>
public Fast2DArray<short> Tables { get; set; } = new Fast2DArray<short>(256, 2);
public Fast2DArray<HuffmanBranch> Tables { get; set; } = new Fast2DArray<HuffmanBranch>(256, 2);
}

internal struct HuffmanBranch
{
public HuffmanBranch(short value)
: this(value, new List<HuffmanBranch>())
{
}

public HuffmanBranch(List<HuffmanBranch> children)
: this(0, children)
{
}

private HuffmanBranch(short value, List<HuffmanBranch> children)
{
this.Index = 0;
this.Value = value;
this.Children = children;
}

public int Index;

public short Value;

public List<HuffmanBranch> Children;

This comment has been minimized.

Copy link
@tocsoft

tocsoft Jun 19, 2017

Member

I was reading the js code and interesting fact is that there seems to only ever be 2 children of any node, and in where they are decoded later it ends up navigating the tree using a series of bools to switching between the 2 trees looking for the first node with a value set.

This comment has been minimized.

Copy link
@JimBobSquarePants

JimBobSquarePants Jun 20, 2017

Author Member

It does seem to be that case. If so we can limit the capacity of our lists.

}
}
83 changes: 69 additions & 14 deletions src/ImageSharp/Formats/Jpeg/Port/JpegDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
namespace ImageSharp.Formats.Jpeg.Port
{
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;

using ImageSharp.Formats.Jpeg.Port.Components;
using ImageSharp.Memory;
Expand Down Expand Up @@ -346,9 +347,9 @@ private void ProcessDefineHuffmanTablesMarker(int remaining)

using (var huffmanData = new Buffer<byte>(remaining))
{
this.InputStream.Read(huffmanData.Array, 1, remaining);
this.InputStream.Skip(1);
this.InputStream.Read(huffmanData.Array, 0, remaining);

int o = 0;
for (int i = 0; i < remaining;)
{
byte huffmanTableSpec = huffmanData[i];
Expand All @@ -357,41 +358,95 @@ private void ProcessDefineHuffmanTablesMarker(int remaining)

for (int j = 0; j < 16; j++)
{
codeLengthSum += codeLengths[j] = huffmanData[o++];
codeLengthSum += codeLengths[j] = huffmanData[j];
}

// TODO: Pooling?
short[] huffmanValues = new short[codeLengthSum];
using (var values = new Buffer<byte>(256))
using (var values = new Buffer<byte>(codeLengthSum))
{
this.InputStream.Read(values.Array, 0, codeLengthSum);

for (int j = 0; j < codeLengthSum; j++)
{
huffmanValues[j] = values[o++];
huffmanValues[j] = values[j];
}
}

i += 17 + codeLengthSum;
i += 17 + codeLengthSum;

this.BuildHuffmanTable(
huffmanTableSpec >> 4 == 0 ? this.dcHuffmanTables : this.acHuffmanTables,
huffmanTableSpec & 15,
codeLengths,
huffmanValues);
this.BuildHuffmanTable(
huffmanTableSpec >> 4 == 0 ? this.dcHuffmanTables : this.acHuffmanTables,
huffmanTableSpec & 15,
codeLengths,
huffmanValues);
}
}
}
}

private void BuildHuffmanTable(HuffmanTables tables, int index, byte[] codeLengths, short[] 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--;
}

Span<short> tableSpan = tables.Tables.GetRowSpan(index);
var code = new Queue<HuffmanBranch>();
code.Enqueue(new HuffmanBranch(new List<HuffmanBranch>()));
HuffmanBranch p = code.Peek();
p.Children = new List<HuffmanBranch>();
HuffmanBranch q;
int k = 0;
try
{
for (int i = 0; i < length; i++)
{
for (int j = 0; j < codeLengths[i]; j++)
{
p = code.Dequeue();
p.Children.Add(new HuffmanBranch(values[k]));
while (p.Index > 0)
{
p = code.Dequeue();

This comment has been minimized.

Copy link
@tocsoft

tocsoft Jun 19, 2017

Member

javascript push/pop acts like a Stack<T> not a Queue<T>

}

p.Index++;

This comment has been minimized.

Copy link
@tocsoft

tocsoft Jun 19, 2017

Member

this is only ever called if p.Index == 0 thus could be switched out to a bool

This comment has been minimized.

Copy link
@JimBobSquarePants

JimBobSquarePants Jun 20, 2017

Author Member

I'll have to double check that

code.Enqueue(p);
while (code.Count <= i)
{
q = new HuffmanBranch(new List<HuffmanBranch>());
code.Enqueue(q);
p.Children.Add(new HuffmanBranch(q.Children));
p = q;
}

k++;
}

if (i + 1 < length)
{
// p here points to last code
q = new HuffmanBranch(new List<HuffmanBranch>());
code.Enqueue(q);
p.Children.Add(new HuffmanBranch(q.Children));
p = q;
}
}

Span<HuffmanBranch> tableSpan = tables.Tables.GetRowSpan(index);

List<HuffmanBranch> result = code.Peek().Children;

This comment has been minimized.

Copy link
@tocsoft

tocsoft Jun 19, 2017

Member

this isn't what the JS code did, it just dropped the entire first HuffmanBranch into tables.Tables[index] assuming tables.Tables is a HuffmanBranch[16] (I got the 16 as huffmanTableSpec & 15 means the index will always be <= 15)

This comment has been minimized.

Copy link
@JimBobSquarePants

JimBobSquarePants Jun 20, 2017

Author Member

Yeah, I was totally wrong here. Needs more sleep in my life

for (int i = 0; i < result.Count; i++)
{
tableSpan[i] = result[i];
}
}
catch (Exception e)
{
throw;
}
}

/// <summary>
Expand Down

0 comments on commit 3728b82

Please sign in to comment.