Skip to content

Commit d12222a

Browse files
JPEG: Fix not decoding images with restart markers or multi huffman tables
1 parent bb5d662 commit d12222a

File tree

1 file changed

+68
-28
lines changed

1 file changed

+68
-28
lines changed

MCGalaxy/util/Imaging/JpegDecoder.cs

Lines changed: 68 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ permissions and limitations under the Licenses.
2222

2323
namespace MCGalaxy.Util.Imaging
2424
{
25-
public class JpegDecoder : ImageDecoder
25+
public unsafe class JpegDecoder : ImageDecoder
2626
{
2727
static byte[] jfifSig = new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }; // "SOI", "APP0"
2828
static byte[] exifSig = new byte[] { 0xFF, 0xD8, 0xFF, 0xE1 }; // "SOI", "APP1"
@@ -49,14 +49,17 @@ public override SimpleBitmap Decode(byte[] src) {
4949
}
5050

5151

52+
const ushort MARKER_FRAME_BEG = 0xFFC0;
53+
const ushort MARKER_TBL_HUFF = 0xFFC4;
54+
const ushort MARKER_RST_MRKR0 = 0xFFD0;
55+
const ushort MARKER_RST_MRKR7 = 0xFFD7;
5256
const ushort MARKER_IMAGE_BEG = 0xFFD8;
5357
const ushort MARKER_IMAGE_END = 0xFFD9;
58+
const ushort MARKER_SCAN_BEG = 0xFFDA;
59+
const ushort MARKER_TBL_QUANT = 0xFFDB;
60+
const ushort MARKER_RST_NTRVL = 0xFFDD;
5461
const ushort MARKER_APP0 = 0xFFE0;
5562
const ushort MARKER_APP15 = 0xFFEF;
56-
const ushort MARKER_TBL_QUANT = 0xFFDB;
57-
const ushort MARKER_TBL_HUFF = 0xFFC4;
58-
const ushort MARKER_FRAME_BEG = 0xFFC0;
59-
const ushort MARKER_SCAN_BEG = 0xFFDA;
6063
const ushort MARKER_COMMENT = 0xFFFE;
6164

6265
void ReadMarkers(byte[] src, SimpleBitmap bmp) {
@@ -71,7 +74,7 @@ void ReadMarkers(byte[] src, SimpleBitmap bmp) {
7174
return;
7275
} else if (marker >= MARKER_APP0 && marker <= MARKER_APP15) {
7376
SkipMarker(src);
74-
} else if (marker == MARKER_COMMENT) {
77+
} else if (marker == MARKER_COMMENT || marker == MARKER_RST_NTRVL) {
7578
SkipMarker(src);
7679
} else if (marker == MARKER_TBL_HUFF) {
7780
ReadHuffmanTable(src);
@@ -99,13 +102,12 @@ void SkipMarker(byte[] src) {
99102
void ReadQuantisationTables(byte[] src) {
100103
int offset = AdvanceOffset(2);
101104
int length = MemUtils.ReadU16_BE(src, offset);
102-
103-
// length *includes* 2 bytes of length
104-
offset = AdvanceOffset(length - 2);
105-
length -= 2;
105+
106+
length -= 2; // length *includes* 2 bytes of length
107+
offset = AdvanceOffset(length);
106108

107109
// Can have more than one quantisation table
108-
while (length != 0)
110+
while (length > 0)
109111
{
110112
if (length < QUANT_DATA_LEN) Fail("quant table too short: " + length);
111113
length -= QUANT_DATA_LEN;
@@ -129,15 +131,25 @@ void ReadQuantisationTables(byte[] src) {
129131
void ReadHuffmanTable(byte[] src) {
130132
int offset = AdvanceOffset(2);
131133
int length = MemUtils.ReadU16_BE(src, offset);
132-
// length *includes* 2 bytes of length
133-
offset = AdvanceOffset(length - 2);
134-
135-
byte flags = src[offset++];
136134

137-
HuffmanTable table = new HuffmanTable();
138-
HuffmanTable[] tables = (flags >> 4) != 0 ? ac_huff_tables : dc_huff_tables;
139-
tables[flags & 0x03] = table;
135+
length -= 2; // length *includes* 2 bytes of length
136+
offset = AdvanceOffset(length);
140137

138+
// Can have more than one huffman table
139+
while (length > 0)
140+
{
141+
byte flags = src[offset++];
142+
143+
HuffmanTable table = new HuffmanTable();
144+
HuffmanTable[] tables = (flags >> 4) != 0 ? ac_huff_tables : dc_huff_tables;
145+
tables[flags & 0x03] = table;
146+
147+
int read = DecodeHuffmanTable(src, table, ref offset);
148+
length -= 1 + read; // 1 byte for flags
149+
}
150+
}
151+
152+
int DecodeHuffmanTable(byte[] src, HuffmanTable table, ref int offset) {
141153
table.firstCodewords = new ushort[HUFF_MAX_BITS];
142154
table.endCodewords = new ushort[HUFF_MAX_BITS];
143155
table.firstOffsets = new ushort[HUFF_MAX_BITS];
@@ -170,7 +182,7 @@ void ReadHuffmanTable(byte[] src) {
170182
code = (code + count) << 1;
171183
}
172184
if (total > HUFF_MAX_VALS) Fail("too many values");
173-
total = 0;
185+
int valueIdx = 0;
174186

175187
// Read values for each codeword.
176188
// Note that although codewords are ordered, values may not be.
@@ -179,9 +191,11 @@ void ReadHuffmanTable(byte[] src) {
179191
{
180192
for (int j = 0; j < counts[i]; j++)
181193
{
182-
table.values[total++] = src[offset++];
194+
table.values[valueIdx++] = src[offset++];
183195
}
184196
}
197+
198+
return HUFF_MAX_BITS + total;
185199
}
186200

187201
void ReadFrameStart(byte[] src, SimpleBitmap bmp) {
@@ -281,7 +295,7 @@ void DecodeMCUs(byte[] src, SimpleBitmap bmp) {
281295

282296
JpegComponent[] comps = this.comps;
283297
int[] block = new int[BLOCK_SIZE];
284-
float[] output = new float[BLOCK_SIZE];
298+
float* output = stackalloc float[BLOCK_SIZE];
285299

286300
YCbCr[] colors = new YCbCr[mcu_w * mcu_h];
287301
for (int i = 0; i < colors.Length; i++)
@@ -293,6 +307,16 @@ void DecodeMCUs(byte[] src, SimpleBitmap bmp) {
293307
for (int mcuY = 0; mcuY < mcus_y; mcuY++)
294308
for (int mcuX = 0; mcuX < mcus_x; mcuX++)
295309
{
310+
// Technically, reset0-7 markers should only occur every # MCUs
311+
// as per the 'restart interval' marker. However, since proper
312+
// spec compliant decoding is unimportant, just hackily decode
313+
if (hit_rst) {
314+
hit_rst = false;
315+
// Align bit reader to next byte, then reset DC prediction value
316+
ConsumeBits(bit_cnt & 0x07);
317+
for (int i = 0; i < comps.Length; i++) comps[i].PredDCValue = 0;
318+
}
319+
296320
for (int i = 0; i < comps.Length; i++)
297321
{
298322
JpegComponent comp = comps[i];
@@ -408,13 +432,13 @@ void ComputeIDCTFactors() {
408432
float cosuv = (float)Math.Cos((2 * xy + 1) * uv * Math.PI / 16.0);
409433

410434
factors[(xy * 8) + uv] = cuv * cosuv;
411-
}
435+
}
412436
}
413437
idct_factors = factors;
414438
}
415439

416-
unsafe void IDCT(int[] block, float[] output) {
417-
float[] factors = idct_factors;
440+
void IDCT(int[] block, float* output) {
441+
float[] factors = idct_factors;
418442
float* tmp = stackalloc float[BLOCK_SAMPLES * BLOCK_SAMPLES];
419443

420444
for (int col = 0; col < BLOCK_SAMPLES; col++)
@@ -446,17 +470,26 @@ unsafe void IDCT(int[] block, float[] output) {
446470

447471
uint bit_buf;
448472
int bit_cnt;
449-
bool end;
473+
bool hit_end, hit_rst;
450474

451475
int ReadBits(byte[] src, int count) {
452-
while (bit_cnt <= 24 && !end) {
476+
while (bit_cnt <= 24 && !hit_end) {
453477
byte next = src[buf_offset++];
454478
// JPEG byte stuffing
455479
// TODO restart markers ?
456480
if (next == 0xFF) {
457481
byte type = src[buf_offset++];
458-
if (type == 0xD9) { end = true; buf_offset -= 2; }
459-
else if (type != 0) Fail("unexpected marker");
482+
483+
if (type == (MARKER_IMAGE_END & 0xFF)) {
484+
next = 0;
485+
hit_end = true;
486+
buf_offset -= 2;
487+
} else if (type >= (MARKER_RST_MRKR0 & 0xFF) && type <= (MARKER_RST_MRKR7 & 0xFF)) {
488+
hit_rst = true;
489+
continue;
490+
} else if (type != 0) {
491+
Fail("unexpected marker");
492+
}
460493
}
461494

462495
bit_buf <<= 8;
@@ -472,6 +505,13 @@ int ReadBits(byte[] src, int count) {
472505
return bits;
473506
}
474507

508+
void ConsumeBits(int count) {
509+
int read = bit_cnt - count;
510+
511+
bit_buf &= (uint)((1 << read) - 1);
512+
bit_cnt -= count;
513+
}
514+
475515
byte ReadHuffman(HuffmanTable table, byte[] src) {
476516
int codeword = 0;
477517
// TODO optimise

0 commit comments

Comments
 (0)