1 // Written in the D programming language.
4 * Copyright: Copyright 2012 -
5 * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 * Authors: Callum Anderson
16 * Jpeg decoder. Great reference for baseline JPEG
17 * deconding: http://www.opennet.ru/docs/formats/jpeg.txt.
20 static struct IMGError
{
25 bool imageComplete
= false;
26 IMGError m_errorState
;
27 TrueColorImage m_image
;
29 @property final auto image() () { pragma(inline
, true); return m_image
; }
30 @property final IMGError
errorState() () const { return m_errorState
; }
32 // Clamp an integer to 0-255 (ubyte)
33 static ubyte clamp() (const int x
) {
35 return (x
< 0 ?
0 : (x
> 0xFF ?
0xFF : cast(ubyte) x
));
38 // Algorithms for upsampling the chroma components, defaults to NEAREST.
40 NEAREST
, // Nearest neighbour (fastest)
41 BILINEAR
, // Bilinear interpolation
44 // Empty constructor, useful for parsing a stream manually
45 this (VFile fl
, in Upsampling algo
=Upsampling
.NEAREST
) {
46 // set the resampling algorithm delegate
47 if (algo
== Upsampling
.NEAREST
) resampleDgt
= &nearestNeighbourResample
;
48 else if (algo
== Upsampling
.BILINEAR
) resampleDgt
= &bilinearResample
;
49 else resampleDgt
= &nearestNeighbourResample
; // just in case
52 if (fl
.rawRead(b
[]).length
!= 1) break;
54 if (imageComplete
) break;
56 if (!imageComplete
) throw new Exception("invalid jpeg image");
59 this (ubyte[] data
, in Upsampling algo
=Upsampling
.NEAREST
) {
60 // set the resampling algorithm delegate
61 if (algo
== Upsampling
.NEAREST
) resampleDgt
= &nearestNeighbourResample
;
62 else if (algo
== Upsampling
.BILINEAR
) resampleDgt
= &bilinearResample
;
63 else resampleDgt
= &nearestNeighbourResample
; // just in case
64 foreach (ubyte b
; data
) {
66 if (imageComplete
) break;
68 if (!imageComplete
) throw new Exception("invalid jpeg image");
71 // parse a single byte
72 void parseByte (ubyte bite
) {
73 segment
.buffer
~= bite
;
74 if (bite
== 0xFF) { markerPending
= true; return; }
76 markerPending
= false;
78 // this is an 0xFF value
79 segment
.buffer
= segment
.buffer
[0..$-1];
81 } else if (bite
>= 0xD0 && bite
<= 0xD7) {
83 segment
.buffer
= segment
.buffer
[0..$-2];
85 } else if (cast(Marker
)bite
== Marker
.EndOfImage
) {
86 previousMarker
= currentMarker
;
87 currentMarker
= cast(Marker
) bite
;
91 previousMarker
= currentMarker
;
92 currentMarker
= cast(Marker
) bite
;
93 segment
= JPGSegment();
97 if (!segment
.headerProcessed
) {
98 if (segment
.buffer
.length
== 2) {
99 segment
.headerLength
= (segment
.buffer
[0] << 8 | segment
.buffer
[1]);
101 } else if (segment
.buffer
.length
== segment
.headerLength
) {
102 debug if (m_logging
) writeln(currentMarker
);
104 segment
.headerProcessed
= true;
105 segment
.buffer
= null;
109 if (currentMarker
== Marker
.StartOfScan
) sosAction(bite
);
115 // Markers courtesy of http://techstumbler.blogspot.com/2008/09/jpeg-marker-codes.html
119 // Start of Frame markers, non-differential, Huffman coding
120 HuffBaselineDCT
= 0xC0,
121 HuffExtSequentialDCT
= 0xC1,
122 HuffProgressiveDCT
= 0xC2,
123 HuffLosslessSeq
= 0xC3,
125 // Start of Frame markers, differential, Huffman coding
126 HuffDiffSequentialDCT
= 0xC5,
127 HuffDiffProgressiveDCT
= 0xC6,
128 HuffDiffLosslessSeq
= 0xC7,
130 // Start of Frame markers, non-differential, arithmetic coding
131 ArthBaselineDCT
= 0xC8,
132 ArthExtSequentialDCT
= 0xC9,
133 ArthProgressiveDCT
= 0xCA,
134 ArthLosslessSeq
= 0xCB,
136 // Start of Frame markers, differential, arithmetic coding
137 ArthDiffSequentialDCT
= 0xCD,
138 ArthDiffProgressiveDCT
= 0xCE,
139 ArthDiffLosslessSeq
= 0xCF,
141 // Huffman table spec
142 HuffmanTableDef
= 0xC4,
144 // Arithmetic table spec
145 ArithmeticTableDef
= 0xCC,
147 // Restart Interval termination
148 RestartIntervalStart
= 0xD0,
149 RestartIntervalEnd
= 0xD7,
155 QuantTableDef
= 0xDB,
156 NumberOfLinesDef
= 0xDC,
157 RestartIntervalDef
= 0xDD,
158 HierarchProgressionDef
= 0xDE,
159 ExpandRefComponents
= 0xDF,
162 Rst0
= 0xD0, Rst1
= 0xD1, Rst2
= 0xD2, Rst3
= 0xD3,
163 Rst4
= 0xD4, Rst5
= 0xD5, Rst6
= 0xD6, Rst7
= 0xD7,
166 App0
= 0xE0, App1
= 0xE1, App2
= 0xE2, App3
= 0xE3,
167 App4
= 0xE4, App5
= 0xE5, App6
= 0xE6, App7
= 0xE7,
168 App8
= 0xE8, App9
= 0xE9, App10
= 0xEA, App11
= 0xEB,
169 App12
= 0xEC, App13
= 0xED, App14
= 0xEE, App15
= 0xEF,
172 JpegExt0
= 0xF0, JpegExt1
= 0xF1, JpegExt2
= 0xF2, JpegExt3
= 0xF3,
173 JpegExt4
= 0xF4, JpegExt5
= 0xF5, JpegExt6
= 0xF6, JpegExt7
= 0xF7,
174 JpegExt8
= 0xF8, JpegExt9
= 0xF9, JpegExtA
= 0xFA, JpegExtB
= 0xFB,
175 JpegExtC
= 0xFC, JpegExtD
= 0xFD,
182 ReservedStart
= 0x02,
186 // Value at dctComponent[ix] should go to grid[block_order[ix]]
187 static immutable ubyte[64] block_order
=
188 [ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
189 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28,
190 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
191 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 ];
193 ulong totalBytesParsed
= 0;
194 ulong segmentBytesParsed
= 0;
195 Marker currentMarker
= Marker
.None
;
196 Marker previousMarker
= Marker
.None
;
197 bool markerPending
= false;
198 void delegate(uint cmpIndex
) resampleDgt
;
200 string format
= "unknown"; // File format (will only do JFIF)
201 ubyte nComponents
, precision
;
204 int id
, // component id
205 qtt
, // quantization table id
206 h_sample
, // horizontal samples
207 v_sample
; // vertical samples
208 ubyte[] data
; // a single MCU of data for this component
209 int x
, y
; // x, y are size of MCU
211 Component
[] components
;
213 // Store the image comment field if any
216 // Quantization Tables (hash map of ubyte[64]'s, indexed by table index)
217 ubyte[][int] quantTable
;
219 // Huffman tables are stored in a hash map
220 ubyte[16] nCodes
; // Number of codes of each bit length (cleared after each table is defined)
222 ubyte index
; // Table index
223 ubyte nBits
; // Number of bits in code
224 short bitCode
; // Actual bit code
226 ubyte[hashKey
] huffmanTable
;
228 // Track the state of a scan segment
230 short cmpIdx
= 0; // Current component index in scan
232 int MCUWidth
, MCUHeight
; // Dimensions of an MCU
233 int nxMCU
, nyMCU
, xMCU
, yMCU
; // Number of MCU's, and current MCU
235 uint buffer
= 0, bufferLength
= 0, needBits
= 0;
236 bool comparing
= true;
237 ubyte[3] dct
, act
, nCmpBlocks
; // dct, act store the DC and AC table indexes for each component
239 int[3] dcTerm
; // DC coefficients for each component
240 int[64] dctComponents
; // DCT coefficients for current component
241 uint dctCmpIndex
= 0, blockNumber
= 0; // DCT coefficient index and current block in MCU
242 int restartInterval
; // How many MCU's are parsed before a restart (reset the DC terms)
243 int MCUSParsed
; // Number of image MCU's parsed, for use with restart interval
245 ScanState scState
; // ditto
248 bool headerProcessed
;
254 debug bool m_logging
;
255 short x
, y
; // These are the final image width and height
257 // Process a segment header
258 void processHeader () {
260 * Remember: first two bytes in the buffer are the header length,
261 * so header info starts at segment.buffer[2]!
263 switch (currentMarker
) {
264 case Marker
.Comment
: // Comment segment
265 comment
= cast(char[])segment
.buffer
[2..$];
266 debug if (m_logging
) writeln("JPEG: Comment: ", comment
);
268 case Marker
.App0
: // App0, indicates JFIF format
269 if (previousMarker
== Marker
.StartOfImage
) format
= "JFIF";
271 case Marker
.RestartIntervalDef
: // Restart interval definition
272 scState
.restartInterval
= cast(int)(segment
.buffer
[2] << 8 | segment
.buffer
[3]);
273 debug if (m_logging
) writeln("JPEG: Restart interval = ", scState
.restartInterval
);
275 case Marker
.QuantTableDef
: // A quantization table definition
276 for (int i
= 2; i
< segment
.buffer
.length
; i
+= 65) {
277 int precision
= (segment
.buffer
[i
] >> 4);
278 int index
= (segment
.buffer
[i
] & 0x0F);
279 quantTable
[index
] = segment
.buffer
[i
+1..i
+1+64].dup
;
280 debug if (m_logging
) writefln("JPEG: Quantization table %s defined", index
);
283 case Marker
.HuffBaselineDCT
: // Baseline frame
284 ubyte precision
= segment
.buffer
[2];
285 y
= cast(short) (segment
.buffer
[3] << 8 | segment
.buffer
[4]);
286 x
= cast(short) (segment
.buffer
[5] << 8 | segment
.buffer
[6]);
287 nComponents
= segment
.buffer
[7];
288 components
.length
= nComponents
;
290 foreach (cmp; 0..nComponents
) {
291 components
[cmp].id
= segment
.buffer
[i
];
292 components
[cmp].h_sample
= (segment
.buffer
[i
+1] >> 4);
293 components
[cmp].v_sample
= (segment
.buffer
[i
+1] & 0x0F);
294 components
[cmp].qtt
= segment
.buffer
[i
+2];
296 debug if (m_logging
) writefln("JPEG: Component %s defined", cmp);
299 case Marker
.HuffProgressiveDCT
: // Progressive JPEG, cannot decode
300 m_errorState
.code
= 1;
301 m_errorState
.message
= "JPG: Progressive JPEG detected, unable to load";
303 case Marker
.HuffmanTableDef
: // Huffman Table Definition, the mapping between bitcodes and Huffman codes
305 while (i
< segment
.buffer
.length
) {
306 import std
.algorithm
: reduce
;
307 ubyte index
= segment
.buffer
[i
]; // Huffman table index
310 auto nCodes
= segment
.buffer
[i
..i
+16]; // Number of codes at each tree depth
311 int totalCodes
= reduce
!("a + b")(0, nCodes
); // Sum up total codes, so we know when we are done
315 ubyte huffmanRow
= 0;
316 short huffmanCol
= 0;
317 while (storedCodes
!= totalCodes
) {
319 * If nCodes is zero, we need to move down the table. The 'table'
320 * is basically a binary tree, seen as an array.
322 while (huffmanRow
< 15 && nCodes
[huffmanRow
] == 0) {
327 if (huffmanRow
< 16) {
328 // Store the code into the hash table, using index, row and bitcode to make the key
329 hashKey key
= { index
:index
, nBits
: cast(ubyte)(huffmanRow
+1), bitCode
: huffmanCol
};
330 huffmanTable
[key
] = segment
.buffer
[i
];
333 --nCodes
[huffmanRow
];
336 } // while storedCodes != totalCodes
339 case Marker
.StartOfScan
: // StartOfScan (image data) header
340 int scanComponents
= segment
.buffer
[2]; // Number of components in the scan
341 if (scanComponents
!= nComponents
) throw new Exception("JPEG: Scan components != image components!");
344 foreach (cmp; 0..scanComponents
) {
345 ubyte id
= cast(ubyte)(segment
.buffer
[i
] - 1);
346 scState
.dct
[id
] = segment
.buffer
[i
+1] >> 4; // Component's DC huffman table
347 scState
.act
[id
] = segment
.buffer
[i
+1] & 0x0F; // Component's AC huffman table
349 // There is more to the header, but it is not needed
351 // Calculate MCU dimensions
352 int v_samp_max
= 0, h_samp_max
= 0;
353 foreach (cmp; components
) {
354 if (cmp.h_sample
> h_samp_max
) h_samp_max
= cmp.h_sample
;
355 if (cmp.v_sample
> v_samp_max
) v_samp_max
= cmp.v_sample
;
357 scState
.MCUWidth
= h_samp_max
*8;
358 scState
.MCUHeight
= v_samp_max
*8;
360 // Number of MCU's in the whole transformed image (the actual image could be smaller)
361 scState
.nxMCU
= x
/ scState
.MCUWidth
;
362 scState
.nyMCU
= y
/ scState
.MCUHeight
;
363 if (x
% scState
.MCUWidth
> 0) ++scState
.nxMCU
;
364 if (y
% scState
.MCUHeight
> 0) ++scState
.nyMCU
;
366 // Allocate the image
367 m_image
= new TrueColorImage(scState
.nxMCU
*scState
.MCUWidth
, scState
.nyMCU
*scState
.MCUHeight
);
369 // Calculate the number of pixels for each component from the number of MCU's and sampling rate
370 foreach (idx
, ref cmp; components
) {
371 // Just make it big enough for a single MCU
372 cmp.x
= cmp.h_sample
*8;
373 cmp.y
= cmp.v_sample
*8;
374 cmp.data
= new ubyte[](cmp.x
*cmp.y
);
375 debug if (m_logging
) writefln("Component %s, x:%s, y:%s", idx
, cmp.x
, cmp.y
);
379 debug if (m_logging
) writeln("JPEG: ProcessHeader called on un-handled segment: ", currentMarker
);
384 // Start of scan (image)
385 void sosAction (ubyte bite
) {
386 // Put the new bite into the buffer
387 scState
.buffer
= scState
.buffer
<< 8 | bite
;
388 scState
.bufferLength
+= 8;
389 segment
.buffer
= null;
390 while (scState
.bufferLength
>= scState
.needBits
) {
391 if (scState
.comparing
) {
392 // Try to get a Huffman code from the buffer
393 ubyte* huffCode
= fetchHuffmanCode(scState
.buffer
, scState
.bufferLength
, scState
.needBits
, scState
.cmpIdx
);
394 if (huffCode
!is null) {
395 // Found a valid huffman code
396 // Our buffer has effectively shrunk by the number of bits we just took
397 scState
.bufferLength
-= scState
.needBits
;
398 scState
.needBits
= 1;
399 processHuffmanCode(*huffCode
);
402 // Failed to get a Huffman code, try with more bits
406 // Not comparing, getting value bits
407 if (scState
.bufferLength
< scState
.needBits
) continue; // Need more bits in the buffer
408 // We have enough bits now to grab the value, so do that
409 int dctComp
= fetchDCTComponent(scState
.buffer
, scState
.bufferLength
, scState
.needBits
);
410 // Clear these bits from the buffer, set flag back to 'comparing'
411 scState
.bufferLength
-= scState
.needBits
;
412 scState
.comparing
= true;
413 // Put the new value into the component array
414 scState
.dctComponents
[scState
.dctCmpIndex
] = dctComp
;
415 ++scState
.dctCmpIndex
; // Increment our index in the components array
416 if (scState
.dctCmpIndex
== 64) endOfBlock(); // If we have reached the last index, this is end of block
417 scState
.needBits
= 1; // Reset the number of bits we need for comparing
419 } // while bufferLength >= needBits
422 // Check the buffer for a valid Huffman code
423 ubyte* fetchHuffmanCode (int buffer
, int bufferLength
, int needBits
, int componentIndex
) {
424 // Create a mask to compare needBits bits in the buffer
425 uint mask
= ((1 << needBits
) - 1) << (bufferLength
-needBits
);
426 ushort bitCode
= cast(ushort) ((mask
& buffer
) >> (bufferLength
- needBits
));
428 ubyte huffIndex
= cast(ubyte) (componentIndex
!= 0);
429 if (scState
.dctCmpIndex
!= 0) {
430 // This is an AC component
432 tableId
= scState
.act
[componentIndex
];
434 // This is a DC component
435 tableId
= scState
.dct
[componentIndex
];
437 hashKey key
= hashKey(huffIndex
, cast(ubyte)needBits
, bitCode
);
438 return (key
in huffmanTable
);
440 } // fetchHuffmanCode
442 // Process a Huffman code
443 void processHuffmanCode (short huffCode
) {
444 if (huffCode
== 0x00) {
446 if (scState
.dctCmpIndex
== 0) {
447 // If we are on the DC term, don't call end of block...
448 ++scState
.dctCmpIndex
; // just increment the index
453 // Not an end of block
454 // The zero run length (not used for the DC component)
455 ubyte preZeros
= cast(ubyte) (huffCode
>> 4);
456 // Increment the index by the number of preceeding zeros
457 scState
.dctCmpIndex
+= preZeros
;
458 // The number of bits we need to get an actual value
459 if (scState
.dctCmpIndex
== 64) {
460 // Check if we are at the end of the block
463 scState
.comparing
= false; // Not comparing bits anymore, waiting for a bitcode
464 scState
.needBits
= cast(uint)(huffCode
& 0x0F); // Number of bits in the bitcode
467 } // processHuffmanCode
469 // Fetch the actual DCT component value
470 int fetchDCTComponent (int buffer
, int bufferLength
, int needBits
) {
471 // Create a mask to get the value from the (int) buffer
472 uint mask
= ((1 << needBits
) - 1) << (bufferLength
-needBits
);
473 short bits
= cast(short) ((mask
& buffer
) >> (bufferLength
- needBits
));
474 // The first bit tells us which side of the value table we are on (- or +)
475 int bit0
= bits
>> (needBits
-1);
476 int offset
= 2^^needBits
;
477 return (bits
& ((1 << (needBits
-1)) - 1)) + (bit0
*offset
/2 - (1-bit0
)*(offset
- 1));
478 } // fetchDCTComponent
480 // Have reached the end of a block, within a scan
482 import std
.conv
: to
;
483 // Convert the DC value from relative to absolute
484 scState
.dctComponents
[0] += scState
.dcTerm
[scState
.cmpIdx
];
485 // Store this block's DC term, to apply to the next block
486 scState
.dcTerm
[scState
.cmpIdx
] = scState
.dctComponents
[0];
487 // Grab the quantization table for this component
488 int[] qTable
= to
!(int[])(quantTable
[components
[scState
.cmpIdx
].qtt
]);
489 // Dequantize the coefficients
490 scState
.dctComponents
[] *= qTable
[];
493 foreach (idx
, elem
; block_order
) block
[elem
] = scState
.dctComponents
[idx
];
494 // Calculate the offset into the component's pixel array
497 // Each component now only holds a single MCU
498 offset
= 8*( (blockNumber
% 2) + (blockNumber
/ 2)*components
[cmpIdx
].x
);
500 // The recieving buffer of the IDCT is then the component's pixel array
501 ubyte* pix
= components
[scState
.cmpIdx
].data
.ptr
+ offset
;
502 // Do the inverse discrete cosine transform
503 foreach(i
; 0..8) colIDCT(block
.ptr
+ i
); // columns
504 foreach(i
; 0..8) rowIDCT(block
.ptr
+ i
*8, pix
+ i
*components
[scState
.cmpIdx
].x
); // rows
505 scState
.dctCmpIndex
= 0;
506 scState
.dctComponents
[] = 0;
507 scState
.comparing
= true;
508 // We have just decoded an 8x8 'block'
509 ++scState
.blockNumber
;
510 if (scState
.blockNumber
== components
[scState
.cmpIdx
].h_sample
*components
[scState
.cmpIdx
].v_sample
) {
511 // All the components in this block have been parsed
512 scState
.blockNumber
= 0;
514 if (scState
.cmpIdx
== nComponents
) {
515 // All components in the MCU have been parsed, so increment
518 ++scState
.MCUSParsed
;
520 if (scState
.xMCU
== scState
.nxMCU
) {
525 } // if done all blocks for this component in the current MCU
526 // Check for restart marker
527 if (scState
.restartInterval
!= 0 && scState
.MCUSParsed
== scState
.restartInterval
) {
528 // We have come up to a restart marker, so reset the DC terms
529 scState
.dcTerm
[] = 0;
530 scState
.MCUSParsed
= 0;
531 // Need to skip all the bits up to the next byte boundary
532 while (scState
.bufferLength
% 8 != 0) --scState
.bufferLength
;
536 // An MCU has been decoded, so resample, convert, and store
538 if (nComponents
== 3) {
539 // Resample if needed
540 if (components
[1].x
!= scState
.MCUWidth
) resampleDgt(1);
541 if (components
[2].x
!= scState
.MCUWidth
) resampleDgt(2);
542 // YCbCr -> RGB conversion
547 // Resample an MCU with nearest neighbour interp
548 void nearestNeighbourResample (uint cmpIndex
) {
549 with (components
[cmpIndex
]) {
550 ubyte[] buffer
= new ubyte[](scState
.MCUWidth
*scState
.MCUHeight
);
551 float x_ratio
= cast(float)(x
-1)/cast(float)(scState
.MCUWidth
);
552 float y_ratio
= cast(float)(y
-1)/cast(float)(scState
.MCUHeight
);
553 foreach (immutable r
; 0..scState
.MCUHeight
) {
554 foreach (immutable c
; 0..scState
.MCUWidth
) {
555 int px
= cast(int)(x_ratio
* cast(float)c
);
556 int py
= cast(int)(y_ratio
* cast(float)r
);
557 buffer
[c
+ scState
.MCUWidth
*r
] = data
[px
+ py
*x
];
562 } // with components[cmpIdx]
565 // Resample an MCU with bilinear interp
566 void bilinearResample (uint cmpIndex
) {
567 with (components
[cmpIndex
]) {
568 ubyte[] buffer
= new ubyte[](scState
.MCUWidth
*scState
.MCUHeight
);
569 float x_ratio
= cast(float)(x
-1)/cast(float)(scState
.MCUWidth
);
570 float y_ratio
= cast(float)(y
-1)/cast(float)(scState
.MCUHeight
);
571 foreach (immutable r
; 0..scState
.MCUHeight
) {
572 foreach (immutable c
; 0..scState
.MCUWidth
) {
573 float px
= (x_ratio
* cast(float)c
);
574 float py
= (y_ratio
* cast(float)r
);
575 int x0
= cast(int)px
;
576 int y0
= cast(int)py
;
580 float fx1
= 1.0f - fx
;
581 float fy1
= 1.0f - fy
;
582 /** Get the locations in the src array of the 2x2 block surrounding (row,col)
587 ubyte p1
= data
[x0
+ y0
*x
];
588 ubyte p2
= data
[(x0
+1) + y0
*x
];
589 ubyte p3
= data
[x0
+ (y0
+1)*x
];
590 ubyte p4
= data
[(x0
+1) + (y0
+1)*x
];
591 int wgt1
= cast(int)(fx1
* fy1
* 256.0f);
592 int wgt2
= cast(int)(fx
* fy1
* 256.0f);
593 int wgt3
= cast(int)(fx1
* fy
* 256.0f);
594 int wgt4
= cast(int)(fx
* fy
* 256.0f);
595 int v
= (p1
* wgt1
+ p2
* wgt2
+ p3
* wgt3
+ p4
* wgt4
) >> 8;
596 buffer
[c
+ scState
.MCUWidth
*r
] = cast(ubyte)v
;
601 } // with components[cmpIdx]
602 } // bilinearResample
604 // Convert YCbCr to RGB an store in output image
607 ubyte[] RGBref
= m_image
.imageData
.bytes
;
608 ubyte[] Yref
= components
[0].data
;
609 ubyte[] Cbref
= components
[1].data
;
610 ubyte[] Crref
= components
[2].data
;
611 int r
, g
, b
, i
= 0, stride
= scState
.MCUWidth
;
612 int ip0
= 0, ipStride
= 0;
614 ip0
= (xMCU
*MCUWidth
+ yMCU
*MCUWidth
*MCUHeight
*nxMCU
)*4;
615 ipStride
= MCUWidth
*nxMCU
*4;
617 foreach (immutable y
; 0..scState
.MCUHeight
) {
618 foreach (immutable x
; 0..scState
.MCUWidth
) {
619 int y_fixed
= (Yref
[i
+x
] << 16) + 32768; // rounding
620 int cr
= Crref
[i
+x
] - 128;
621 int cb
= Cbref
[i
+x
] - 128;
622 r
= y_fixed
+ cr
*cast(int)(1.40200f * 65536 + 0.5);
623 g
= y_fixed
- cr
*cast(int)(0.71414f * 65536 + 0.5) -
624 cb
*cast(int)(0.34414f * 65536 + 0.5);
625 b
= y_fixed
+ cb
*cast(int)(1.77200f * 65536 + 0.5);
630 RGBref
[ip0
+x
*4..ip0
+x
*4+3] = [clamp(r
), clamp(g
), clamp(b
)];
631 RGBref
[ip0
+x
*4+3] = 255;
641 * Crop the image back to its real size (JPEG encoders can increase
642 * increase the dimensions to make them divisible by 8 for the DCT
644 //image.resize(x, y, Image.ResizeAlgo.CROP);
645 if (m_image
.width
!= x || m_image
.height
!= y
) {
646 TrueColorImage newImg
= new TrueColorImage(x
, y
);
647 foreach (immutable dy
; 0..y
) {
648 newImg
.imageData
.colors
[dy
*newImg
.width
..dy
*newImg
.width
+x
] = m_image
.imageData
.colors
[dy
*m_image
.width
..dy
*m_image
.width
+x
];
653 scState
= ScanState();
656 components
= null; //.clear;
657 imageComplete
= true;
661 * The following inverse discrete cosine transform (IDCT) voodoo comes from:
662 * stbi-1.33 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c
664 void colIDCT (int* block
) {
665 int x0
, x1
, x2
, x3
, t0
, t1
, t2
, t3
, p1
, p2
, p3
, p4
, p5
;
666 if (block
[8] == 0 && block
[16] == 0 && block
[24] == 0 && block
[32] == 0 && block
[40] == 0 && block
[48] == 0 && block
[56] == 0) {
667 int dcterm
= block
[0] << 2;
668 block
[0] = block
[8] = block
[16] = block
[24] =
669 block
[32] = block
[40] = block
[48] = block
[56] = dcterm
;
674 p1
= (p2
+p3
)*cast(int)(0.5411961f * 4096 + 0.5);
675 t2
= p1
+ p3
*cast(int)(-1.847759065f * 4096 + 0.5);
676 t3
= p1
+ p2
*cast(int)( 0.765366865f * 4096 + 0.5);
693 p5
= (p3
+p4
)*cast(int)( 1.175875602f * 4096 + 0.5);
694 t0
= t0
*cast(int)( 0.298631336f * 4096 + 0.5);
695 t1
= t1
*cast(int)( 2.053119869f * 4096 + 0.5);
696 t2
= t2
*cast(int)( 3.072711026f * 4096 + 0.5);
697 t3
= t3
*cast(int)( 1.501321110f * 4096 + 0.5);
698 p1
= p5
+ p1
*cast(int)(-0.899976223f * 4096 + 0.5);
699 p2
= p5
+ p2
*cast(int)(-2.562915447f * 4096 + 0.5);
700 p3
= p3
*cast(int)(-1.961570560f * 4096 + 0.5);
701 p4
= p4
*cast(int)(-0.390180644f * 4096 + 0.5);
710 block
[0] = (x0
+t3
) >> 10;
711 block
[56] = (x0
-t3
) >> 10;
712 block
[8] = (x1
+t2
) >> 10;
713 block
[48] = (x1
-t2
) >> 10;
714 block
[16] = (x2
+t1
) >> 10;
715 block
[40] = (x2
-t1
) >> 10;
716 block
[24] = (x3
+t0
) >> 10;
717 block
[32] = (x3
-t0
) >> 10;
721 void rowIDCT (int* block
, ubyte* outData
) {
722 int x0
, x1
, x2
, x3
, t0
, t1
, t2
, t3
, p1
, p2
, p3
, p4
, p5
;
725 p1
= (p2
+p3
)*cast(int)(0.5411961f * 4096 + 0.5);
726 t2
= p1
+ p3
*cast(int)(-1.847759065f * 4096 + 0.5);
727 t3
= p1
+ p2
*cast(int)( 0.765366865f * 4096 + 0.5);
744 p5
= (p3
+p4
)*cast(int)( 1.175875602f * 4096 + 0.5);
745 t0
= t0
*cast(int)( 0.298631336f * 4096 + 0.5);
746 t1
= t1
*cast(int)( 2.053119869f * 4096 + 0.5);
747 t2
= t2
*cast(int)( 3.072711026f * 4096 + 0.5);
748 t3
= t3
*cast(int)( 1.501321110f * 4096 + 0.5);
749 p1
= p5
+ p1
*cast(int)(-0.899976223f * 4096 + 0.5);
750 p2
= p5
+ p2
*cast(int)(-2.562915447f * 4096 + 0.5);
751 p3
= p3
*cast(int)(-1.961570560f * 4096 + 0.5);
752 p4
= p4
*cast(int)(-0.390180644f * 4096 + 0.5);
757 x0
+= 65536 + (128<<17);
758 x1
+= 65536 + (128<<17);
759 x2
+= 65536 + (128<<17);
760 x3
+= 65536 + (128<<17);
761 outData
[0] = clamp((x0
+t3
) >> 17);
762 outData
[7] = clamp((x0
-t3
) >> 17);
763 outData
[1] = clamp((x1
+t2
) >> 17);
764 outData
[6] = clamp((x1
-t2
) >> 17);
765 outData
[2] = clamp((x2
+t1
) >> 17);
766 outData
[5] = clamp((x2
-t1
) >> 17);
767 outData
[3] = clamp((x3
+t0
) >> 17);
768 outData
[4] = clamp((x3
-t0
) >> 17);