1 // Written in the D programming language. 2 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 imaged.jpeg; 10 11 import 12 std.file, 13 std.stdio, 14 std.math, 15 std.algorithm, 16 std.conv, 17 undead.stream; 18 19 import 20 imaged.image; 21 22 // Clamp an integer to 0-255 (ubyte) 23 ubyte clamp(const int x) 24 { 25 return (x < 0) ? 0 : ((x > 0xFF) ? 0xFF : cast(ubyte) x); 26 } 27 28 29 /** 30 * Jpeg decoder. Great reference for baseline JPEG 31 * deconding: http://www.opennet.ru/docs/formats/jpeg.txt. 32 */ 33 class JpegDecoder : Decoder 34 { 35 36 // Algorithms for upsampling the chroma components, defaults to NEAREST. 37 enum Upsampling 38 { 39 NEAREST, // Nearest neighbour (fastest) 40 BILINEAR // Bilinear interpolation 41 } 42 43 44 // Empty constructor, useful for parsing a stream manually 45 this(in bool logging = false, in Upsampling algo = Upsampling.NEAREST) 46 { 47 m_logging = logging; 48 49 // Set the resampling algorithm delegate 50 if (algo == Upsampling.NEAREST) 51 { 52 resampleDgt = &nearestNeighbourResample; 53 } 54 else if (algo == Upsampling.BILINEAR) 55 { 56 resampleDgt = &bilinearResample; 57 } 58 } 59 60 61 // Constructor taking a filename 62 this(in string filename, in bool logging = false, in Upsampling algo = Upsampling.NEAREST) 63 { 64 this(logging, algo); 65 parseFile(filename); 66 } 67 68 69 // Parse a single byte 70 override void parseByte(ubyte bite) 71 { 72 segment.buffer ~= bite; 73 74 if (bite == 0xFF) 75 { 76 markerPending = true; 77 return; 78 } 79 80 if (markerPending) 81 { 82 markerPending = false; 83 84 if (bite == 0x00) // This is an 0xFF value 85 { 86 segment.buffer = segment.buffer[0..$-1]; 87 bite = 0xFF; 88 } 89 else if (bite >= 0xD0 && bite <= 0xD7) // Restart marker 90 { 91 segment.buffer = segment.buffer[0..$-2]; 92 return; 93 } 94 else if (cast(Marker)bite == Marker.EndOfImage) 95 { 96 previousMarker = currentMarker; 97 currentMarker = cast(Marker) bite; 98 endOfImage(); 99 return; 100 } 101 else 102 { 103 previousMarker = currentMarker; 104 currentMarker = cast(Marker) bite; 105 segment = JPGSegment(); 106 return; 107 } 108 } 109 110 if (!segment.headerProcessed) 111 { 112 if (segment.buffer.length == 2) 113 { 114 segment.headerLength = (segment.buffer[0] << 8 | segment.buffer[1]); 115 return; 116 } 117 else if (segment.buffer.length == segment.headerLength) 118 { 119 debug 120 { 121 if (m_logging) writeln(currentMarker); 122 } 123 124 processHeader(); 125 segment.headerProcessed = true; 126 segment.buffer.destroy; 127 return; 128 } 129 } 130 else 131 { 132 if (currentMarker == Marker.StartOfScan) 133 { 134 sosAction(bite); 135 } 136 } 137 138 totalBytesParsed ++; 139 } // parse 140 141 142 private: 143 144 // Markers courtesy of http://techstumbler.blogspot.com/2008/09/jpeg-marker-codes.html 145 enum Marker 146 { 147 None = 0x00, 148 149 // Start of Frame markers, non-differential, Huffman coding 150 HuffBaselineDCT = 0xC0, 151 HuffExtSequentialDCT = 0xC1, 152 HuffProgressiveDCT = 0xC2, 153 HuffLosslessSeq = 0xC3, 154 155 // Start of Frame markers, differential, Huffman coding 156 HuffDiffSequentialDCT = 0xC5, 157 HuffDiffProgressiveDCT = 0xC6, 158 HuffDiffLosslessSeq = 0xC7, 159 160 // Start of Frame markers, non-differential, arithmetic coding 161 ArthBaselineDCT = 0xC8, 162 ArthExtSequentialDCT = 0xC9, 163 ArthProgressiveDCT = 0xCA, 164 ArthLosslessSeq = 0xCB, 165 166 // Start of Frame markers, differential, arithmetic coding 167 ArthDiffSequentialDCT = 0xCD, 168 ArthDiffProgressiveDCT = 0xCE, 169 ArthDiffLosslessSeq = 0xCF, 170 171 // Huffman table spec 172 HuffmanTableDef = 0xC4, 173 174 // Arithmetic table spec 175 ArithmeticTableDef = 0xCC, 176 177 // Restart Interval termination 178 RestartIntervalStart = 0xD0, 179 RestartIntervalEnd = 0xD7, 180 181 // Other markers 182 StartOfImage = 0xD8, 183 EndOfImage = 0xD9, 184 StartOfScan = 0xDA, 185 QuantTableDef = 0xDB, 186 NumberOfLinesDef = 0xDC, 187 RestartIntervalDef = 0xDD, 188 HierarchProgressionDef = 0xDE, 189 ExpandRefComponents = 0xDF, 190 191 // Restarts 192 Rst0 = 0xD0, Rst1 = 0xD1, Rst2 = 0xD2, Rst3 = 0xD3, 193 Rst4 = 0xD4, Rst5 = 0xD5, Rst6 = 0xD6, Rst7 = 0xD7, 194 195 // App segments 196 App0 = 0xE0, App1 = 0xE1, App2 = 0xE2, App3 = 0xE3, 197 App4 = 0xE4, App5 = 0xE5, App6 = 0xE6, App7 = 0xE7, 198 App8 = 0xE8, App9 = 0xE9, App10 = 0xEA, App11 = 0xEB, 199 App12 = 0xEC, App13 = 0xED, App14 = 0xEE, App15 = 0xEF, 200 201 // Jpeg Extensions 202 JpegExt0 = 0xF0, JpegExt1 = 0xF1, JpegExt2 = 0xF2, JpegExt3 = 0xF3, 203 JpegExt4 = 0xF4, JpegExt5 = 0xF5, JpegExt6 = 0xF6, JpegExt7 = 0xF7, 204 JpegExt8 = 0xF8, JpegExt9 = 0xF9, JpegExtA = 0xFA, JpegExtB = 0xFB, 205 JpegExtC = 0xFC, JpegExtD = 0xFD, 206 207 // Comments 208 Comment = 0xFE, 209 210 // Reserved 211 ArithTemp = 0x01, 212 ReservedStart = 0x02, 213 ReservedEnd = 0xBF 214 }; 215 216 // Value at dctComponent[ix] should go to grid[block_order[ix]] 217 immutable static ubyte[] block_order = 218 [ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 219 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 220 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 221 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 ]; 222 223 ulong totalBytesParsed = 0; 224 ulong segmentBytesParsed = 0; 225 Marker currentMarker = Marker.None; 226 Marker previousMarker = Marker.None; 227 bool markerPending = false; 228 void delegate(uint cmpIndex) resampleDgt; 229 230 string format = "unknown"; // File format (will only do JFIF) 231 ubyte nComponents, precision; 232 233 struct Component 234 { 235 int id, // component id 236 qtt, // quantization table id 237 h_sample, // horizontal samples 238 v_sample; // vertical samples 239 ubyte[] data; // a single MCU of data for this component 240 int x, y; // x, y are size of MCU 241 } 242 Component[] components; 243 244 // Store the image comment field if any 245 char[] comment; 246 247 // Quantization Tables (hash map of ubyte[64]'s, indexed by table index) 248 ubyte[][int] quantTable; 249 250 // Huffman tables are stored in a hash map 251 ubyte[16] nCodes; // Number of codes of each bit length .destroyed after each table is defined) 252 struct hashKey 253 { 254 ubyte index; // Table index 255 ubyte nBits; // Number of bits in code 256 short bitCode; // Actual bit code 257 } 258 ubyte[hashKey] huffmanTable; 259 260 // Track the state of a scan segment 261 struct ScanState 262 { 263 short cmpIdx = 0; // Current component index in scan 264 265 int MCUWidth, MCUHeight; // Dimensions of an MCU 266 int nxMCU, nyMCU, xMCU, yMCU; // Number of MCU's, and current MCU 267 268 uint buffer = 0, bufferLength = 0, needBits = 0; 269 bool comparing = true; 270 ubyte[3] dct, act, nCmpBlocks; // dct, act store the DC and AC table indexes for each component 271 272 int[3] dcTerm; // DC coefficients for each component 273 int[64] dctComponents; // DCT coefficients for current component 274 uint dctCmpIndex = 0, blockNumber = 0; // DCT coefficient index and current block in MCU 275 int restartInterval; // How many MCU's are parsed before a restart (reset the DC terms) 276 int MCUSParsed; // Number of image MCU's parsed, for use with restart interval 277 } 278 ScanState scState; // ditto 279 280 struct JPGSegment 281 { 282 bool headerProcessed; 283 int headerLength; 284 ubyte[] buffer; 285 } 286 JPGSegment segment; 287 288 bool m_logging; 289 short x, y; // These are the final image width and height 290 291 // Process a segment header 292 void processHeader() 293 { 294 /** 295 * Remember: first two bytes in the buffer are the header length, 296 * so header info starts at segment.buffer[2]! 297 */ 298 switch(currentMarker) 299 { 300 case Marker.Comment: // Comment segment 301 { 302 comment = cast(char[])segment.buffer[2..$]; 303 304 debug 305 { 306 if (m_logging) writeln("JPEG: Comment: ", comment); 307 } 308 break; 309 } 310 case Marker.App0: // App0, indicates JFIF format 311 { 312 if (previousMarker == Marker.StartOfImage) 313 { 314 format = "JFIF"; 315 } 316 break; 317 } 318 case Marker.RestartIntervalDef: // Restart interval definition 319 { 320 scState.restartInterval = cast(int) (segment.buffer[2] << 8 | segment.buffer[3]); 321 322 debug 323 { 324 if (m_logging) writeln("JPEG: Restart interval = ", scState.restartInterval); 325 } 326 break; 327 } 328 case Marker.QuantTableDef: // A quantization table definition 329 { 330 for (int i = 2; i < segment.buffer.length; i += 65) 331 { 332 int precision = (segment.buffer[i] >> 4); 333 int index = (segment.buffer[i] & 0x0F); 334 quantTable[index] = segment.buffer[i+1..i+1+64].dup; 335 336 debug 337 { 338 if (m_logging) writefln("JPEG: Quantization table %s defined", index); 339 } 340 } 341 break; 342 } 343 case Marker.HuffBaselineDCT: // Baseline frame 344 { 345 ubyte precision = segment.buffer[2]; 346 y = cast(short) (segment.buffer[3] << 8 | segment.buffer[4]); 347 x = cast(short) (segment.buffer[5] << 8 | segment.buffer[6]); 348 nComponents = segment.buffer[7]; 349 components.length = nComponents; 350 351 int i = 8; 352 foreach(cmp; 0..nComponents) 353 { 354 components[cmp].id = segment.buffer[i]; 355 components[cmp].h_sample = (segment.buffer[i+1] >> 4); 356 components[cmp].v_sample = (segment.buffer[i+1] & 0x0F); 357 components[cmp].qtt = segment.buffer[i+2]; 358 i += 3; 359 360 debug 361 { 362 if (m_logging) writefln("JPEG: Component %s defined", cmp); 363 } 364 } 365 break; 366 } 367 case Marker.HuffProgressiveDCT: // Progressive JPEG, cannot decode 368 { 369 m_errorState.code = 1; 370 m_errorState.message = "JPG: Progressive JPEG detected, unable to load"; 371 break; 372 } 373 case Marker.HuffmanTableDef: // Huffman Table Definition, the mapping between bitcodes and Huffman codes 374 { 375 int i = 2; 376 while (i < segment.buffer.length) 377 { 378 ubyte index = segment.buffer[i]; // Huffman table index 379 i ++; 380 381 auto nCodes = segment.buffer[i..i+16]; // Number of codes at each tree depth 382 int totalCodes = reduce!("a + b")(0, nCodes); // Sum up total codes, so we know when we are done 383 int storedCodes = 0; 384 i += 16; 385 386 ubyte huffmanRow = 0; 387 short huffmanCol = 0; 388 while (storedCodes != totalCodes) 389 { 390 /** 391 * If nCodes is zero, we need to move down the table. The 'table' 392 * is basically a binary tree, seen as an array. 393 */ 394 while (huffmanRow < 15 && nCodes[huffmanRow] == 0) 395 { 396 huffmanRow ++; 397 huffmanCol *= 2; 398 } 399 400 if (huffmanRow < 16) 401 { // Store the code into the hash table, using index, row and bitcode to make the key 402 hashKey key = { index:index, nBits: cast(ubyte)(huffmanRow+1), bitCode: huffmanCol}; 403 huffmanTable[key] = segment.buffer[i]; 404 storedCodes ++; 405 huffmanCol ++; 406 nCodes[huffmanRow] --; 407 i ++; 408 } 409 } // while storedCodes != totalCodes 410 } 411 break; 412 } 413 case Marker.StartOfScan: // StartOfScan (image data) header 414 { 415 int scanComponents = segment.buffer[2]; // Number of components in the scan 416 417 if (scanComponents != nComponents) 418 { 419 throw new Exception("JPEG: Scan components != image components!"); 420 } 421 422 int i = 3; 423 foreach (cmp; 0..scanComponents) 424 { 425 ubyte id = cast(ubyte)(segment.buffer[i] - 1); 426 scState.dct[id] = segment.buffer[i+1] >> 4; // Component's DC huffman table 427 scState.act[id] = segment.buffer[i+1] & 0x0F; // Component's AC huffman table 428 } 429 // There is more to the header, but it is not needed 430 431 432 // Calculate MCU dimensions 433 int v_samp_max = 0, h_samp_max = 0; 434 foreach (cmp; components) 435 { 436 if (cmp.h_sample > h_samp_max) 437 h_samp_max = cmp.h_sample; 438 if (cmp.v_sample > v_samp_max) 439 v_samp_max = cmp.v_sample; 440 } 441 scState.MCUWidth = h_samp_max*8; 442 scState.MCUHeight = v_samp_max*8; 443 444 // Number of MCU's in the whole transformed image (the actual image could be smaller) 445 scState.nxMCU = x / scState.MCUWidth; 446 scState.nyMCU = y / scState.MCUHeight; 447 if (x % scState.MCUWidth > 0) 448 scState.nxMCU ++; 449 if (y % scState.MCUHeight > 0) 450 scState.nyMCU ++; 451 452 // Allocate the image 453 m_image = new Img!(Px.R8G8B8)(scState.nxMCU*scState.MCUWidth, 454 scState.nyMCU*scState.MCUHeight); 455 456 457 // Calculate the number of pixels for each component from the number of MCU's and sampling rate 458 foreach (idx, ref cmp; components) 459 { 460 // Just make it big enough for a single MCU 461 cmp.x = cmp.h_sample*8; 462 cmp.y = cmp.v_sample*8; 463 cmp.data = new ubyte[](cmp.x*cmp.y); 464 465 debug 466 { 467 if (m_logging) writefln("Component %s, x:%s, y:%s", idx, cmp.x, cmp.y); 468 } 469 } 470 break; 471 } 472 default: 473 { 474 debug 475 { 476 if (m_logging) writeln("JPEG: ProcessHeader called on un-handled segment: ", currentMarker); 477 } 478 break; 479 } 480 } 481 482 } 483 484 485 // Start of scan (image) 486 void sosAction(ubyte bite) 487 { 488 // Put the new bite into the buffer 489 scState.buffer = scState.buffer << 8 | bite ; 490 scState.bufferLength += 8; 491 segment.buffer.destroy; 492 493 while (scState.bufferLength >= scState.needBits) 494 { 495 if (scState.comparing) 496 { 497 // Try to get a Huffman code from the buffer 498 ubyte* huffCode = fetchHuffmanCode(scState.buffer, 499 scState.bufferLength, 500 scState.needBits, 501 scState.cmpIdx); 502 503 if (huffCode !is null) // Found a valid huffman code 504 { 505 // Our buffer has effectively shrunk by the number of bits we just took 506 scState.bufferLength -= scState.needBits; 507 scState.needBits = 1; 508 509 processHuffmanCode(*huffCode); 510 continue; 511 } 512 else // Failed to get a Huffman code, try with more bits 513 { 514 scState.needBits ++; 515 } 516 517 } 518 else // Not comparing, getting value bits 519 { 520 if (scState.bufferLength < scState.needBits) continue; // Need more bits in the buffer 521 522 // We have enough bits now to grab the value, so do that 523 int dctComp = fetchDCTComponent(scState.buffer, 524 scState.bufferLength, 525 scState.needBits); 526 527 //.destroy these bits from the buffer, set flag back to 'comparing' 528 scState.bufferLength -= scState.needBits; 529 scState.comparing = true; 530 531 // Put the new value into the component array 532 scState.dctComponents[scState.dctCmpIndex] = dctComp; 533 534 scState.dctCmpIndex ++; // Increment our index in the components array 535 if (scState.dctCmpIndex == 64) endOfBlock(); // If we have reached the last index, this is end of block 536 scState.needBits = 1; // Reset the number of bits we need for comparing 537 538 } // if !comparing 539 } // while bufferLength >= needBits 540 } // sosAction 541 542 543 // Check the buffer for a valid Huffman code 544 ubyte* fetchHuffmanCode(int buffer, int bufferLength, int needBits, int componentIndex) 545 { 546 // Create a mask to compare needBits bits in the buffer 547 uint mask = ((1 << needBits) - 1) << (bufferLength-needBits); 548 ushort bitCode = cast(ushort) ((mask & buffer) >> (bufferLength - needBits)); 549 550 ubyte tableId = 0; 551 ubyte huffIndex = cast(ubyte) (componentIndex != 0); 552 553 if (scState.dctCmpIndex != 0) // This is an AC component 554 { 555 huffIndex += 16; 556 tableId = scState.act[componentIndex]; 557 } 558 else // This is a DC component 559 { 560 tableId = scState.dct[componentIndex]; 561 } 562 563 hashKey key = hashKey(huffIndex, cast(ubyte)needBits, bitCode); 564 return (key in huffmanTable); 565 566 } // fetchHuffmanCode 567 568 569 // Process a Huffman code 570 void processHuffmanCode(short huffCode) 571 { 572 if (huffCode == 0x00) // END OF BLOCK 573 { 574 if (scState.dctCmpIndex == 0) // If we are on the DC term, don't call end of block... 575 { 576 scState.dctCmpIndex ++; // just increment the index 577 } 578 else 579 { 580 endOfBlock(); 581 } 582 583 } 584 else // Not an end of block 585 { 586 // The zero run length (not used for the DC component) 587 ubyte preZeros = cast(ubyte) (huffCode >> 4); 588 589 // Increment the index by the number of preceeding zeros 590 scState.dctCmpIndex += preZeros; 591 592 // The number of bits we need to get an actual value 593 if (scState.dctCmpIndex == 64) // Check if we are at the end of the block 594 { 595 endOfBlock(); 596 } 597 else 598 { 599 scState.comparing = false; // Not comparing bits anymore, waiting for a bitcode 600 scState.needBits = cast(uint) (huffCode & 0x0F); // Number of bits in the bitcode 601 } 602 } 603 } // processHuffmanCode 604 605 606 // Fetch the actual DCT component value 607 int fetchDCTComponent(int buffer, int bufferLength, int needBits) 608 { 609 // Create a mask to get the value from the (int) buffer 610 uint mask = ((1 << needBits) - 1) << (bufferLength-needBits); 611 short bits = cast(short) ((mask & buffer) >> (bufferLength - needBits)); 612 613 // The first bit tells us which side of the value table we are on (- or +) 614 int bit0 = bits >> (needBits-1); 615 int offset = 2^^needBits; 616 return (bits & ((1 << (needBits-1)) - 1)) + (bit0*offset/2 - (1-bit0)*(offset - 1)); 617 } // fetchDCTComponent 618 619 620 // Have reached the end of a block, within a scan 621 void endOfBlock() 622 { 623 // Convert the DC value from relative to absolute 624 scState.dctComponents[0] += scState.dcTerm[scState.cmpIdx]; 625 626 // Store this block's DC term, to apply to the next block 627 scState.dcTerm[scState.cmpIdx] = scState.dctComponents[0]; 628 629 // Grab the quantization table for this component 630 int[] qTable = to!(int[])(quantTable[components[scState.cmpIdx].qtt]); 631 632 // Dequantize the coefficients 633 scState.dctComponents[] *= qTable[]; 634 635 // Un zig-zag 636 int[64] block; 637 foreach (idx, elem; block_order) 638 { 639 block[elem] = scState.dctComponents[idx]; 640 } 641 642 // Calculate the offset into the component's pixel array 643 int offset = 0; 644 with (scState) 645 { 646 // Each component now only holds a single MCU 647 offset = 8*( (blockNumber % 2) + (blockNumber / 2)*components[cmpIdx].x); 648 } 649 650 // The recieving buffer of the IDCT is then the component's pixel array 651 ubyte* pix = components[scState.cmpIdx].data.ptr + offset; 652 653 // Do the inverse discrete cosine transform 654 foreach(i; 0..8) colIDCT(block.ptr + i); // columns 655 foreach(i; 0..8) rowIDCT(block.ptr + i*8, pix + i*components[scState.cmpIdx].x); // rows 656 657 scState.dctCmpIndex = 0; 658 scState.dctComponents[] = 0; 659 scState.comparing = true; 660 661 // We have just decoded an 8x8 'block' 662 scState.blockNumber ++; 663 664 if (scState.blockNumber == components[scState.cmpIdx].h_sample*components[scState.cmpIdx].v_sample) 665 { 666 // All the components in this block have been parsed 667 scState.blockNumber = 0; 668 scState.cmpIdx ++; 669 670 if (scState.cmpIdx == nComponents) 671 { 672 // All components in the MCU have been parsed, so increment 673 endOfMCU(); 674 scState.cmpIdx = 0; 675 scState.MCUSParsed ++; 676 scState.xMCU ++; 677 if (scState.xMCU == scState.nxMCU) 678 { 679 scState.xMCU = 0; 680 scState.yMCU ++; 681 } 682 } 683 } // if done all blocks for this component in the current MCU 684 685 // Check for restart marker 686 if (scState.restartInterval != 0 && scState.MCUSParsed == scState.restartInterval) 687 { 688 // We have come up to a restart marker, so reset the DC terms 689 scState.dcTerm[] = 0; 690 scState.MCUSParsed = 0; 691 692 // Need to skip all the bits up to the next byte boundary 693 while (scState.bufferLength % 8 != 0) scState.bufferLength --; 694 } 695 } // endOfBlock 696 697 698 // An MCU has been decoded, so resample, convert, and store 699 void endOfMCU() 700 { 701 if (nComponents == 3) 702 { 703 // Resample if needed 704 if (components[1].x != scState.MCUWidth) 705 resampleDgt(1); 706 if (components[2].x != scState.MCUWidth) 707 resampleDgt(2); 708 709 // YCbCr -> RGB conversion 710 YCrCBtoRGB(); 711 } 712 } 713 714 715 // Resample an MCU with nearest neighbour interp 716 void nearestNeighbourResample(uint cmpIndex) 717 { 718 with(components[cmpIndex]) 719 { 720 ubyte[] buffer = new ubyte[](scState.MCUWidth*scState.MCUHeight); 721 float x_ratio = cast(float)(x-1)/cast(float)(scState.MCUWidth); 722 float y_ratio = cast(float)(y-1)/cast(float)(scState.MCUHeight); 723 724 foreach(r; 0..scState.MCUHeight) 725 { 726 foreach(c; 0..scState.MCUWidth) 727 { 728 int px = cast(int)(x_ratio * cast(float)c); 729 int py = cast(int)(y_ratio * cast(float)r); 730 buffer[c + scState.MCUWidth*r] = data[px + py*x]; 731 } // cols 732 } // rows 733 734 data.destroy; 735 data = buffer; 736 } // with components[cmpIdx] 737 } 738 739 740 // Resample an MCU with bilinear interp 741 void bilinearResample(uint cmpIndex) 742 { 743 with(components[cmpIndex]) 744 { 745 ubyte[] buffer = new ubyte[](scState.MCUWidth*scState.MCUHeight); 746 float x_ratio = cast(float)(x-1)/cast(float)(scState.MCUWidth); 747 float y_ratio = cast(float)(y-1)/cast(float)(scState.MCUHeight); 748 749 foreach(r; 0..scState.MCUHeight) 750 { 751 foreach(c; 0..scState.MCUWidth) 752 { 753 float px = (x_ratio * cast(float)c); 754 float py = (y_ratio * cast(float)r); 755 756 int x0 = cast(int)px; 757 int y0 = cast(int)py; 758 759 // Weighting factors 760 float fx = px - x0; 761 float fy = py - y0; 762 float fx1 = 1.0f - fx; 763 float fy1 = 1.0f - fy; 764 765 /** Get the locations in the src array of the 2x2 block surrounding (row,col) 766 * 01 ------- 11 767 * | (row,col) | 768 * 00 ------- 10 769 */ 770 ubyte p1 = data[x0 + y0*x]; 771 ubyte p2 = data[(x0+1) + y0*x]; 772 ubyte p3 = data[x0 + (y0+1)*x]; 773 ubyte p4 = data[(x0+1) + (y0+1)*x]; 774 775 int wgt1 = cast(int)(fx1 * fy1 * 256.0f); 776 int wgt2 = cast(int)(fx * fy1 * 256.0f); 777 int wgt3 = cast(int)(fx1 * fy * 256.0f); 778 int wgt4 = cast(int)(fx * fy * 256.0f); 779 780 int v = (p1 * wgt1 + p2 * wgt2 + p3 * wgt3 + p4 * wgt4) >> 8; 781 buffer[c + scState.MCUWidth*r] = cast(ubyte)v; 782 783 } // cols 784 } // rows 785 786 data.destroy; 787 data = buffer; 788 } // with components[cmpIdx] 789 } // bilinearResample 790 791 792 // Convert YCbCr to RGB an store in output image 793 void YCrCBtoRGB() 794 { 795 // Convert to RGB 796 ubyte[] RGBref = m_image.pixels; 797 ubyte[] Yref = components[0].data; 798 ubyte[] Cbref = components[1].data; 799 ubyte[] Crref = components[2].data; 800 int r, g, b, i = 0, stride = scState.MCUWidth; 801 802 int ip0 = 0, ipStride = 0; 803 with(scState) 804 { 805 ip0 = (xMCU*MCUWidth + yMCU*MCUWidth*MCUHeight*nxMCU)*3; 806 ipStride = MCUWidth*nxMCU*3; 807 } 808 809 foreach(y; 0..scState.MCUHeight) 810 { 811 foreach(x; 0..scState.MCUWidth) 812 { 813 814 int y_fixed = (Yref[i+x] << 16) + 32768; // rounding 815 int cr = Crref[i+x] - 128; 816 int cb = Cbref[i+x] - 128; 817 r = y_fixed + cr*cast(int)(1.40200f * 65536 + 0.5); 818 g = y_fixed - cr*cast(int)(0.71414f * 65536 + 0.5) - 819 cb*cast(int)(0.34414f * 65536 + 0.5); 820 b = y_fixed + cb*cast(int)(1.77200f * 65536 + 0.5); 821 r >>= 16; 822 g >>= 16; 823 b >>= 16; 824 825 RGBref[ip0+x*3..ip0+x*3+3] = [clamp(r), clamp(g), clamp(b)]; 826 } 827 i += stride; 828 ip0 += ipStride; 829 } 830 } // YCbCrtoRGB 831 832 833 // End of Image 834 void endOfImage() 835 { 836 /** 837 * Crop the image back to its real size (JPEG encoders can increase 838 * increase the dimensions to make them divisible by 8 for the DCT 839 */ 840 image.resize(x, y, Image.ResizeAlgo.CROP); 841 842 //.destroy some fields 843 scState = ScanState(); 844 quantTable.destroy; 845 huffmanTable.destroy; 846 components.destroy; 847 848 } // eoiAction 849 850 851 /** 852 * The following inverse discrete cosine transform (IDCT) voodoo comes from: 853 * stbi-1.33 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c 854 */ 855 void colIDCT(int* block) 856 { 857 int x0,x1,x2,x3,t0,t1,t2,t3,p1,p2,p3,p4,p5; 858 859 if (block[8] == 0 && block[16] == 0 && block[24] == 0 && block[32] == 0 && 860 block[40] == 0 && block[48] == 0 && block[56] == 0) 861 { 862 863 int dcterm = block[0] << 2; 864 block[0] = block[8] = block[16] = block[24] = 865 block[32] = block[40] = block[48] = block[56] = dcterm; 866 return; 867 } 868 869 p2 = block[16]; 870 p3 = block[48]; 871 p1 = (p2+p3)*cast(int)(0.5411961f * 4096 + 0.5); 872 t2 = p1 + p3*cast(int)(-1.847759065f * 4096 + 0.5); 873 t3 = p1 + p2*cast(int)( 0.765366865f * 4096 + 0.5); 874 p2 = block[0]; 875 p3 = block[32]; 876 t0 = (p2+p3) << 12; 877 t1 = (p2-p3) << 12; 878 x0 = t0+t3; 879 x3 = t0-t3; 880 x1 = t1+t2; 881 x2 = t1-t2; 882 t0 = block[56]; 883 t1 = block[40]; 884 t2 = block[24]; 885 t3 = block[8]; 886 p3 = t0+t2; 887 p4 = t1+t3; 888 p1 = t0+t3; 889 p2 = t1+t2; 890 p5 = (p3+p4)*cast(int)( 1.175875602f * 4096 + 0.5); 891 t0 = t0*cast(int)( 0.298631336f * 4096 + 0.5); 892 t1 = t1*cast(int)( 2.053119869f * 4096 + 0.5); 893 t2 = t2*cast(int)( 3.072711026f * 4096 + 0.5); 894 t3 = t3*cast(int)( 1.501321110f * 4096 + 0.5); 895 p1 = p5 + p1*cast(int)(-0.899976223f * 4096 + 0.5); 896 p2 = p5 + p2*cast(int)(-2.562915447f * 4096 + 0.5); 897 p3 = p3*cast(int)(-1.961570560f * 4096 + 0.5); 898 p4 = p4*cast(int)(-0.390180644f * 4096 + 0.5); 899 t3 += p1+p4; 900 t2 += p2+p3; 901 t1 += p2+p4; 902 t0 += p1+p3; 903 904 x0 += 512; 905 x1 += 512; 906 x2 += 512; 907 x3 += 512; 908 block[0] = (x0+t3) >> 10; 909 block[56] = (x0-t3) >> 10; 910 block[8] = (x1+t2) >> 10; 911 block[48] = (x1-t2) >> 10; 912 block[16] = (x2+t1) >> 10; 913 block[40] = (x2-t1) >> 10; 914 block[24] = (x3+t0) >> 10; 915 block[32] = (x3-t0) >> 10; 916 } // IDCT_1D_COL 917 918 // ditto 919 void rowIDCT(int* block, ubyte* outData) 920 { 921 int x0,x1,x2,x3,t0,t1,t2,t3,p1,p2,p3,p4,p5; 922 923 p2 = block[2]; 924 p3 = block[6]; 925 p1 = (p2+p3)*cast(int)(0.5411961f * 4096 + 0.5); 926 t2 = p1 + p3*cast(int)(-1.847759065f * 4096 + 0.5); 927 t3 = p1 + p2*cast(int)( 0.765366865f * 4096 + 0.5); 928 p2 = block[0]; 929 p3 = block[4]; 930 t0 = (p2+p3) << 12; 931 t1 = (p2-p3) << 12; 932 x0 = t0+t3; 933 x3 = t0-t3; 934 x1 = t1+t2; 935 x2 = t1-t2; 936 t0 = block[7]; 937 t1 = block[5]; 938 t2 = block[3]; 939 t3 = block[1]; 940 p3 = t0+t2; 941 p4 = t1+t3; 942 p1 = t0+t3; 943 p2 = t1+t2; 944 p5 = (p3+p4)*cast(int)( 1.175875602f * 4096 + 0.5); 945 t0 = t0*cast(int)( 0.298631336f * 4096 + 0.5); 946 t1 = t1*cast(int)( 2.053119869f * 4096 + 0.5); 947 t2 = t2*cast(int)( 3.072711026f * 4096 + 0.5); 948 t3 = t3*cast(int)( 1.501321110f * 4096 + 0.5); 949 p1 = p5 + p1*cast(int)(-0.899976223f * 4096 + 0.5); 950 p2 = p5 + p2*cast(int)(-2.562915447f * 4096 + 0.5); 951 p3 = p3*cast(int)(-1.961570560f * 4096 + 0.5); 952 p4 = p4*cast(int)(-0.390180644f * 4096 + 0.5); 953 t3 += p1+p4; 954 t2 += p2+p3; 955 t1 += p2+p4; 956 t0 += p1+p3; 957 958 x0 += 65536 + (128<<17); 959 x1 += 65536 + (128<<17); 960 x2 += 65536 + (128<<17); 961 x3 += 65536 + (128<<17); 962 963 outData[0] = clamp((x0+t3) >> 17); 964 outData[7] = clamp((x0-t3) >> 17); 965 outData[1] = clamp((x1+t2) >> 17); 966 outData[6] = clamp((x1-t2) >> 17); 967 outData[2] = clamp((x2+t1) >> 17); 968 outData[5] = clamp((x2-t1) >> 17); 969 outData[3] = clamp((x3+t0) >> 17); 970 outData[4] = clamp((x3-t0) >> 17); 971 } // IDCT_1D_ROW 972 973 } // JpegDecoder