@@ -22,7 +22,7 @@ permissions and limitations under the Licenses.
2222
2323namespace 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