d2dimage: trying png and jpeg if there's no vga
[dd2d.git] / jpeg.d
blob3781c1338038d3be45b0751287f0e6a0a6c019e6
1 // Written in the D programming language.
3 /**
4 * Copyright: Copyright 2012 -
5 * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 * Authors: Callum Anderson
7 * Date: June 6, 2012
8 */
9 module jpeg;
11 import iv.vfs;
12 import arsd.color;
15 /**
16 * Jpeg decoder. Great reference for baseline JPEG
17 * deconding: http://www.opennet.ru/docs/formats/jpeg.txt.
19 class JpegDecoder {
20 static struct IMGError {
21 string message;
22 int code;
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) {
34 pragma(inline, true);
35 return (x < 0 ? 0 : (x > 0xFF ? 0xFF : cast(ubyte) x));
38 // Algorithms for upsampling the chroma components, defaults to NEAREST.
39 enum Upsampling {
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
50 ubyte[1] b;
51 for (;;) {
52 if (fl.rawRead(b[]).length != 1) break;
53 parseByte(b.ptr[0]);
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) {
65 parseByte(b);
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; }
75 if (markerPending) {
76 markerPending = false;
77 if (bite == 0x00) {
78 // this is an 0xFF value
79 segment.buffer = segment.buffer[0..$-1];
80 bite = 0xFF;
81 } else if (bite >= 0xD0 && bite <= 0xD7) {
82 // restart marker
83 segment.buffer = segment.buffer[0..$-2];
84 return;
85 } else if (cast(Marker)bite == Marker.EndOfImage) {
86 previousMarker = currentMarker;
87 currentMarker = cast(Marker) bite;
88 endOfImage();
89 return;
90 } else {
91 previousMarker = currentMarker;
92 currentMarker = cast(Marker) bite;
93 segment = JPGSegment();
94 return;
97 if (!segment.headerProcessed) {
98 if (segment.buffer.length == 2) {
99 segment.headerLength = (segment.buffer[0] << 8 | segment.buffer[1]);
100 return;
101 } else if (segment.buffer.length == segment.headerLength) {
102 debug if (m_logging) writeln(currentMarker);
103 processHeader();
104 segment.headerProcessed = true;
105 segment.buffer = null;
106 return;
108 } else {
109 if (currentMarker == Marker.StartOfScan) sosAction(bite);
111 ++totalBytesParsed;
112 } // parse
114 private:
115 // Markers courtesy of http://techstumbler.blogspot.com/2008/09/jpeg-marker-codes.html
116 enum Marker {
117 None = 0x00,
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,
151 // Other markers
152 StartOfImage = 0xD8,
153 EndOfImage = 0xD9,
154 StartOfScan = 0xDA,
155 QuantTableDef = 0xDB,
156 NumberOfLinesDef = 0xDC,
157 RestartIntervalDef = 0xDD,
158 HierarchProgressionDef = 0xDE,
159 ExpandRefComponents = 0xDF,
161 // Restarts
162 Rst0 = 0xD0, Rst1 = 0xD1, Rst2 = 0xD2, Rst3 = 0xD3,
163 Rst4 = 0xD4, Rst5 = 0xD5, Rst6 = 0xD6, Rst7 = 0xD7,
165 // App segments
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,
171 // Jpeg Extensions
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,
177 // Comments
178 Comment = 0xFE,
180 // Reserved
181 ArithTemp = 0x01,
182 ReservedStart = 0x02,
183 ReservedEnd = 0xBF
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;
203 struct Component {
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
214 char[] comment;
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)
221 struct hashKey {
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
229 struct ScanState {
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
247 struct JPGSegment {
248 bool headerProcessed;
249 int headerLength;
250 ubyte[] buffer;
252 JPGSegment segment;
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);
267 break;
268 case Marker.App0: // App0, indicates JFIF format
269 if (previousMarker == Marker.StartOfImage) format = "JFIF";
270 break;
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);
274 break;
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);
282 break;
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;
289 int i = 8;
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];
295 i += 3;
296 debug if (m_logging) writefln("JPEG: Component %s defined", cmp);
298 break;
299 case Marker.HuffProgressiveDCT: // Progressive JPEG, cannot decode
300 m_errorState.code = 1;
301 m_errorState.message = "JPG: Progressive JPEG detected, unable to load";
302 break;
303 case Marker.HuffmanTableDef: // Huffman Table Definition, the mapping between bitcodes and Huffman codes
304 int i = 2;
305 while (i < segment.buffer.length) {
306 import std.algorithm : reduce;
307 ubyte index = segment.buffer[i]; // Huffman table index
308 ++i;
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
312 int storedCodes = 0;
313 i += 16;
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) {
323 ++huffmanRow;
324 huffmanCol *= 2;
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];
331 ++storedCodes;
332 ++huffmanCol;
333 --nCodes[huffmanRow];
334 ++i;
336 } // while storedCodes != totalCodes
338 break;
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!");
343 int i = 3;
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);
377 break;
378 default:
379 debug if (m_logging) writeln("JPEG: ProcessHeader called on un-handled segment: ", currentMarker);
380 break;
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);
400 continue;
401 } else {
402 // Failed to get a Huffman code, try with more bits
403 ++scState.needBits;
405 } else {
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
418 } // if !comparing
419 } // while bufferLength >= needBits
420 } // sosAction
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));
427 ubyte tableId = 0;
428 ubyte huffIndex = cast(ubyte) (componentIndex != 0);
429 if (scState.dctCmpIndex != 0) {
430 // This is an AC component
431 huffIndex += 16;
432 tableId = scState.act[componentIndex];
433 } else {
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) {
445 // END OF BLOCK
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
449 } else {
450 endOfBlock();
452 } else {
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
461 endOfBlock();
462 } else {
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
481 void endOfBlock () {
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[];
491 // Un zig-zag
492 int[64] block;
493 foreach (idx, elem; block_order) block[elem] = scState.dctComponents[idx];
494 // Calculate the offset into the component's pixel array
495 int offset = 0;
496 with (scState) {
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;
513 ++scState.cmpIdx;
514 if (scState.cmpIdx == nComponents) {
515 // All components in the MCU have been parsed, so increment
516 endOfMCU();
517 scState.cmpIdx = 0;
518 ++scState.MCUSParsed;
519 ++scState.xMCU;
520 if (scState.xMCU == scState.nxMCU) {
521 scState.xMCU = 0;
522 ++scState.yMCU;
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;
534 } // endOfBlock
536 // An MCU has been decoded, so resample, convert, and store
537 void endOfMCU () {
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
543 YCrCBtoRGB();
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];
558 } // cols
559 } // rows
560 //data.clear;
561 data = buffer;
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;
577 // Weighting factors
578 float fx = px - x0;
579 float fy = py - y0;
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)
583 * 01 ------- 11
584 * | (row,col) |
585 * 00 ------- 10
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;
597 } // cols
598 } // rows
599 //data.clear;
600 data = buffer;
601 } // with components[cmpIdx]
602 } // bilinearResample
604 // Convert YCbCr to RGB an store in output image
605 void YCrCBtoRGB () {
606 // Convert to RGB
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;
613 with (scState) {
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);
626 r >>= 16;
627 g >>= 16;
628 b >>= 16;
630 RGBref[ip0+x*4..ip0+x*4+3] = [clamp(r), clamp(g), clamp(b)];
631 RGBref[ip0+x*4+3] = 255;
633 i += stride;
634 ip0 += ipStride;
636 } // YCbCrtoRGB
638 // End of Image
639 void endOfImage () {
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];
650 m_image = newImg;
652 // Clear some fields
653 scState = ScanState();
654 quantTable.clear;
655 huffmanTable.clear;
656 components = null; //.clear;
657 imageComplete = true;
658 } // eoiAction
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;
670 return;
672 p2 = block[16];
673 p3 = block[48];
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);
677 p2 = block[0];
678 p3 = block[32];
679 t0 = (p2+p3) << 12;
680 t1 = (p2-p3) << 12;
681 x0 = t0+t3;
682 x3 = t0-t3;
683 x1 = t1+t2;
684 x2 = t1-t2;
685 t0 = block[56];
686 t1 = block[40];
687 t2 = block[24];
688 t3 = block[8];
689 p3 = t0+t2;
690 p4 = t1+t3;
691 p1 = t0+t3;
692 p2 = t1+t2;
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);
702 t3 += p1+p4;
703 t2 += p2+p3;
704 t1 += p2+p4;
705 t0 += p1+p3;
706 x0 += 512;
707 x1 += 512;
708 x2 += 512;
709 x3 += 512;
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;
718 } // IDCT_1D_COL
720 // ditto
721 void rowIDCT (int* block, ubyte* outData) {
722 int x0, x1, x2, x3, t0, t1, t2, t3, p1, p2, p3, p4, p5;
723 p2 = block[2];
724 p3 = block[6];
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);
728 p2 = block[0];
729 p3 = block[4];
730 t0 = (p2+p3) << 12;
731 t1 = (p2-p3) << 12;
732 x0 = t0+t3;
733 x3 = t0-t3;
734 x1 = t1+t2;
735 x2 = t1-t2;
736 t0 = block[7];
737 t1 = block[5];
738 t2 = block[3];
739 t3 = block[1];
740 p3 = t0+t2;
741 p4 = t1+t3;
742 p1 = t0+t3;
743 p2 = t1+t2;
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);
753 t3 += p1+p4;
754 t2 += p2+p3;
755 t1 += p2+p4;
756 t0 += p1+p3;
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);
769 } // IDCT_1D_ROW
770 } // JpegDecoder