Skip to content

Commit d094566

Browse files
JPEG: More work on decoder
1 parent d0f03c4 commit d094566

File tree

1 file changed

+160
-45
lines changed

1 file changed

+160
-45
lines changed

MCGalaxy/util/Imaging/JpegDecoder.cs

Lines changed: 160 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -50,46 +50,38 @@ public override SimpleBitmap Decode(byte[] src) {
5050
const ushort MARKER_IMAGE_BEG = 0xFFD8;
5151
const ushort MARKER_IMAGE_END = 0xFFD9;
5252
const ushort MARKER_APP0 = 0xFFE0;
53-
const ushort MARKER_APP1 = 0xFFE1;
53+
const ushort MARKER_APP15 = 0xFFEF;
5454
const ushort MARKER_TBL_QUANT = 0xFFDB;
5555
const ushort MARKER_TBL_HUFF = 0xFFC4;
5656
const ushort MARKER_FRAME_BEG = 0xFFC0;
5757
const ushort MARKER_SCAN_BEG = 0xFFDA;
58+
const ushort MARKER_COMMENT = 0xFFFE;
5859

5960
void ReadMarkers(byte[] src, SimpleBitmap bmp) {
6061
for (;;)
6162
{
6263
int offset = AdvanceOffset(2);
6364
ushort marker = MemUtils.ReadU16_BE(src, offset);
6465

65-
switch (marker)
66-
{
67-
case MARKER_IMAGE_BEG:
68-
break; // Nothing to do
69-
case MARKER_IMAGE_END:
70-
return;
71-
case MARKER_APP0:
72-
case MARKER_APP1:
73-
SkipMarker(src);
74-
break;
75-
76-
case MARKER_TBL_HUFF:
77-
ReadHuffmanTable(src);
78-
break;
79-
case MARKER_TBL_QUANT:
80-
ReadQuantisationTables(src);
81-
break;
82-
case MARKER_FRAME_BEG:
83-
ReadFrameStart(src);
84-
break;
85-
case MARKER_SCAN_BEG:
86-
ReadScanStart(src);
87-
DecodeMCUs(src);
88-
break;
89-
90-
default:
91-
Fail("unknown marker:" + marker.ToString("X4"));
92-
break;
66+
if (marker == MARKER_IMAGE_BEG) {
67+
// Nothing to do
68+
} else if (marker == MARKER_IMAGE_END) {
69+
return;
70+
} else if (marker >= MARKER_APP0 && marker <= MARKER_APP15) {
71+
SkipMarker(src);
72+
} else if (marker == MARKER_COMMENT) {
73+
SkipMarker(src);
74+
} else if (marker == MARKER_TBL_HUFF) {
75+
ReadHuffmanTable(src);
76+
} else if (marker == MARKER_TBL_QUANT) {
77+
ReadQuantisationTables(src);
78+
} else if (marker == MARKER_FRAME_BEG) {
79+
ReadFrameStart(src, bmp);
80+
} else if (marker == MARKER_SCAN_BEG) {
81+
ReadScanStart(src);
82+
DecodeMCUs(src, bmp);
83+
} else {
84+
Fail("unknown marker:" + marker.ToString("X4"));
9385
}
9486
}
9587
}
@@ -137,10 +129,12 @@ void ReadHuffmanTable(byte[] src) {
137129
// length *includes* 2 bytes of length
138130
offset = AdvanceOffset(length - 2);
139131

140-
// TODO multi tables
141132
byte flags = src[offset++];
142133

143-
HuffmanTable table;
134+
HuffmanTable table = new HuffmanTable();
135+
HuffmanTable[] tables = (flags >> 4) != 0 ? ac_huff_tables : dc_huff_tables;
136+
tables[flags & 0x03] = table;
137+
144138
table.firstCodewords = new ushort[HUFF_MAX_BITS];
145139
table.endCodewords = new ushort[HUFF_MAX_BITS];
146140
table.firstOffsets = new ushort[HUFF_MAX_BITS];
@@ -150,11 +144,11 @@ void ReadHuffmanTable(byte[] src) {
150144
// Codewords are ordered, so consider this example tree:
151145
// 2 of length 2, 3 of length 3, 1 of length 4
152146
// Codewords produced would be: 00,01 100,101,110, 1110
153-
int code = 0;
147+
int code = 0;
154148
int total = 0;
155149
byte[] counts = new byte[HUFF_MAX_BITS];
156150

157-
for (int i = 0; i < HUFF_MAX_BITS; i++)
151+
for (int i = 0; i < HUFF_MAX_BITS; i++)
158152
{
159153
byte count = src[offset++];
160154
if (count > (1 << (i+1))) Fail("too many codewords for bit length");
@@ -167,11 +161,8 @@ void ReadHuffmanTable(byte[] src) {
167161
// Last codeword is actually: code + (count - 1)
168162
// However, when decoding we peform < against this value though, so need to add 1 here.
169163
// This way, don't need to special case bit lengths with 0 codewords when decoding.
170-
//
171164
if (count != 0) {
172165
table.endCodewords[i] = (ushort)(code + count);
173-
} else {
174-
table.endCodewords[i] = 0;
175166
}
176167
code = (code + count) << 1;
177168
}
@@ -181,28 +172,27 @@ void ReadHuffmanTable(byte[] src) {
181172
// Read values for each codeword.
182173
// Note that although codewords are ordered, values may not be.
183174
// Some values may also not be assigned to any codeword.
184-
for (int i = 0; i < counts.Length; i++)
175+
for (int i = 0; i < counts.Length; i++)
185176
{
186177
for (int j = 0; j < counts[i]; j++)
187178
{
188179
table.values[total++] = src[offset++];
189180
}
190181
}
191-
192-
HuffmanTable[] tables = (flags >> 4) != 0 ? ac_huff_tables : dc_huff_tables;
193-
tables[flags & 0x03] = table;
194182
}
195183

196-
void ReadFrameStart(byte[] src) {
184+
void ReadFrameStart(byte[] src, SimpleBitmap bmp) {
197185
int offset = AdvanceOffset(2);
198186
int length = MemUtils.ReadU16_BE(src, offset);
199187
// length *includes* 2 bytes of length
200188
offset = AdvanceOffset(length - 2);
201189

202190
byte bits = src[offset + 0];
203191
if (bits != 8) Fail("bits per sample");
204-
int height = MemUtils.ReadU16_BE(src, offset + 1);
205-
int width = MemUtils.ReadU16_BE(src, offset + 3);
192+
193+
bmp.Height = MemUtils.ReadU16_BE(src, offset + 1);
194+
bmp.Width = MemUtils.ReadU16_BE(src, offset + 3);
195+
bmp.AllocatePixels();
206196

207197
byte numComps = src[offset + 5];
208198
if (!(numComps == 1 || numComps == 3)) Fail("num components");
@@ -251,14 +241,138 @@ void SetHuffTables(byte compID, byte tables) {
251241

252242
comps[i].DCHuffTable = (byte)(tables >> 4);
253243
comps[i].ACHuffTable = (byte)(tables & 0x0F);
244+
comps[i].PredDCValue = 0;
254245
return;
255246
}
256247
Fail("unknown scan component");
257248
}
258249

259-
void DecodeMCUs(byte[] src) {
250+
static byte[] zigzag_to_linear = new byte[64]
251+
{
252+
0, 1, 8, 16, 9, 2, 3, 10,
253+
17, 24, 32, 25, 18, 11, 4, 5,
254+
12, 19, 26, 33, 40, 48, 41, 34,
255+
27, 20, 13, 6, 7, 14, 21, 28,
256+
35, 42, 49, 56, 57, 50, 43, 36,
257+
29, 22, 15, 23, 30, 37, 44, 51,
258+
58, 59, 52, 45, 38, 31, 39, 46,
259+
53, 60, 61, 54, 47, 55, 62, 63,
260+
};
261+
262+
void DecodeMCUs(byte[] src, SimpleBitmap bmp) {
263+
int mcus_x = (bmp.Width + 7) / 8;
264+
int mcus_y = (bmp.Height + 7) / 8;
265+
bit_offset = AdvanceOffset(0);
266+
267+
JpegComponent[] comps = this.comps;
268+
int[] block = new int[64];
269+
270+
for (int y = 0; y < mcus_y; y++)
271+
for (int x = 0; x < mcus_x; x++)
272+
{
273+
for (int i = 0; i < comps.Length; i++)
274+
{
275+
// DC value is relative to DC value from prior block
276+
var table = dc_huff_tables[comps[i].DCHuffTable];
277+
int dc_code = ReadHuffman(table, src);
278+
int dc_delta = ReadBiasedValue(src, dc_code);
279+
280+
int dc_value = comps[i].PredDCValue + dc_delta;
281+
comps[i].PredDCValue = dc_value;
282+
283+
byte[] dequant = quant_tables[comps[i].QuantTable];
284+
for (int j = 0; j < block.Length; j++) block[j] = 0;
285+
block[0] = dc_value * dequant[0];
286+
287+
// 63 AC values
288+
table = ac_huff_tables[comps[i].ACHuffTable];
289+
int idx = 1;
290+
do {
291+
int code = ReadHuffman(table, src);
292+
if (code == 0) break;
293+
294+
int bits = code & 0x0F;
295+
int num_zeros = code >> 4;
296+
297+
if (bits == 0) {
298+
if (code == 0) break; // 0 value - end of block
299+
// TODO is this right?
300+
if (num_zeros != 15) Fail("too many zeroes");
301+
idx += 16;
302+
} else {
303+
idx += num_zeros;
304+
int lin = zigzag_to_linear[idx];
305+
block[lin] = ReadBiasedValue(src, bits) * dequant[idx];
306+
idx++;
307+
}
308+
} while (idx < 64);
309+
310+
Fail("DE");
311+
}
312+
}
260313
Fail("MCUs");
261314
}
315+
316+
uint bit_buf;
317+
int bit_cnt;
318+
int bit_offset;
319+
320+
int ReadBits(byte[] src, int count) {
321+
while (bit_cnt <= 24) {
322+
byte next = src[bit_offset++];
323+
// JPEG byte stuffing
324+
// TODO restart markers ?
325+
if (next == 0xFF) {
326+
byte type = src[bit_offset++];
327+
if (type != 0) Fail("unexpected marker");
328+
}
329+
330+
bit_buf <<= 8;
331+
bit_buf |= next;
332+
bit_cnt += 8;
333+
}
334+
335+
int read = bit_cnt - count;
336+
int bits = (int)(bit_buf >> read);
337+
338+
bit_buf &= (uint)((1 << read) - 1);
339+
bit_cnt -= count;
340+
return bits;
341+
}
342+
343+
byte ReadHuffman(HuffmanTable table, byte[] src) {
344+
int codeword = 0;
345+
// TODO optimise
346+
for (int i = 0; i < HUFF_MAX_BITS; i++)
347+
{
348+
codeword <<= 1;
349+
codeword |= ReadBits(src, 1);
350+
351+
if (codeword < table.endCodewords[i]) {
352+
int offset = table.firstOffsets[i] + (codeword - table.firstCodewords[i]);
353+
byte value = table.values[offset];
354+
return value;
355+
}
356+
}
357+
358+
Fail("no huffman code");
359+
return 0;
360+
}
361+
362+
int ReadBiasedValue(byte[] src, int bits) {
363+
if (bits == 0) return 0;
364+
365+
int value = ReadBits(src, bits);
366+
int midpoint = 1 << (bits - 1);
367+
368+
// E.g. for two bits, bits/values are:
369+
// 00, 01, 10, 11
370+
// -3, -2, 2, 3
371+
if (value < midpoint) {
372+
value += (-1 << bits) + 1;
373+
}
374+
return value;
375+
}
262376
}
263377

264378
struct JpegComponent
@@ -269,9 +383,10 @@ struct JpegComponent
269383
public byte QuantTable;
270384
public byte ACHuffTable;
271385
public byte DCHuffTable;
386+
public int PredDCValue;
272387
}
273388

274-
struct HuffmanTable
389+
class HuffmanTable
275390
{
276391
// Maximum of 256 values/symbols and 16 bit length codes
277392

0 commit comments

Comments
 (0)