1 /* 2 Copyright (c) 2015-2021 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 /** 30 * Decode JPEG images 31 * 32 * Copyright: Timur Gafarov 2015-2021. 33 * License: $(LINK2 boost.org/LICENSE_1_0.txt, Boost License 1.0). 34 * Authors: Timur Gafarov 35 */ 36 module dlib.image.io.jpeg; 37 38 import std.stdio; 39 import std.algorithm; 40 import std..string; 41 import std.traits; 42 import std.math; 43 import dlib.core.memory; 44 import dlib.core.stream; 45 import dlib.core.compound; 46 import dlib.core.bitio; 47 import dlib.container.array; 48 import dlib.filesystem.local; 49 import dlib.image.color; 50 import dlib.image.image; 51 52 /* 53 * Simple JPEG decoder 54 * 55 * Limitations: 56 * - Doesn't support progressive JPEG 57 * - Doesn't perform chroma interpolation 58 * - Doesn't read EXIF metadata 59 */ 60 61 // Uncomment this to see debug messages 62 //version = JPEGDebug; 63 64 T readNumeric(T) (InputStream istrm, Endian endian = Endian.Little) 65 if (is(T == ubyte)) 66 { 67 ubyte b; 68 istrm.readBytes(&b, 1); 69 return b; 70 } 71 72 T readNumeric(T) (InputStream istrm, Endian endian = Endian.Little) 73 if (is(T == ushort)) 74 { 75 union U16 76 { 77 ubyte[2] asBytes; 78 ushort asUshort; 79 } 80 U16 u16; 81 istrm.readBytes(u16.asBytes.ptr, 2); 82 version(LittleEndian) 83 { 84 if (endian == Endian.Big) 85 return u16.asUshort.swapEndian16; 86 else 87 return u16.asUshort; 88 } 89 else 90 { 91 if (endian == Endian.Little) 92 return u16.asUshort.swapEndian16; 93 else 94 return u16.asUshort; 95 } 96 } 97 98 char[size] readChars(size_t size) (InputStream istrm) 99 { 100 char[size] chars; 101 istrm.readBytes(chars.ptr, size); 102 return chars; 103 } 104 105 /* 106 * JPEG-related Huffman coding 107 */ 108 109 struct HuffmanCode 110 { 111 ushort bits; 112 ushort length; 113 114 auto bitString() 115 { 116 return .bitString(bits, length); 117 } 118 } 119 120 struct HuffmanTableEntry 121 { 122 HuffmanCode code; 123 ubyte value; 124 } 125 126 Array!char bitString(T)(T n, uint len = 1) if (isIntegral!T) 127 { 128 Array!char arr; 129 130 const int size = T.sizeof * 8; 131 132 bool s = 0; 133 for (int a = 0; a < size; a++) 134 { 135 bool bit = n >> (size - 1); 136 if (bit) 137 s = 1; 138 if (s) 139 { 140 arr.append(bit + '0'); 141 } 142 n <<= 1; 143 } 144 145 while (arr.length < len) 146 arr.appendLeft('0'); 147 148 return arr; 149 } 150 151 struct HuffmanTreeNode 152 { 153 HuffmanTreeNode* parent; 154 HuffmanTreeNode* left; 155 HuffmanTreeNode* right; 156 ubyte ch; 157 uint freq; 158 bool blank = true; 159 160 this( 161 HuffmanTreeNode* leftNode, 162 HuffmanTreeNode* rightNode, 163 ubyte symbol, 164 uint frequency, 165 bool isBlank) 166 { 167 parent = null; 168 left = leftNode; 169 right = rightNode; 170 171 if (left !is null) 172 left.parent = &this; 173 if (right !is null) 174 right.parent = &this; 175 176 ch = symbol; 177 freq = frequency; 178 blank = isBlank; 179 } 180 181 bool isLeaf() 182 { 183 return (left is null && right is null); 184 } 185 186 void free() 187 { 188 if (left !is null) 189 { 190 left.free(); 191 Delete(left); 192 } 193 194 if (right !is null) 195 { 196 right.free(); 197 Delete(right); 198 } 199 } 200 } 201 202 HuffmanTreeNode* emptyNode() 203 { 204 return New!HuffmanTreeNode(null, null, cast(ubyte)0, 0, false); 205 } 206 207 HuffmanTreeNode* treeFromTable(Array!(HuffmanTableEntry) table) 208 { 209 HuffmanTreeNode* root = emptyNode(); 210 211 foreach(i, v; table.data) 212 treeAddCode(root, v.code, v.value); 213 214 return root; 215 } 216 217 void treeAddCode(HuffmanTreeNode* root, HuffmanCode code, ubyte value) 218 { 219 HuffmanTreeNode* node = root; 220 auto bs = code.bitString; 221 foreach(bit; bs.data) 222 { 223 if (bit == '0') 224 { 225 if (node.left is null) 226 { 227 node.left = emptyNode(); 228 node.left.parent = node; 229 } 230 231 node = node.left; 232 } 233 else if (bit == '1') 234 { 235 if (node.right is null) 236 { 237 node.right = emptyNode(); 238 node.right.parent = node; 239 } 240 241 node = node.right; 242 } 243 } 244 assert (node !is null); 245 node.ch = value; 246 bs.free(); 247 } 248 249 /* 250 * JPEG-related data types 251 */ 252 253 enum JPEGMarkerType 254 { 255 Unknown, 256 SOI, 257 SOF0, 258 SOF1, 259 SOF2, 260 DHT, 261 DQT, 262 DRI, 263 SOS, 264 RSTn, 265 APP0, 266 APPn, 267 COM, 268 EOI 269 } 270 271 struct JPEGImage 272 { 273 struct JFIF 274 { 275 ubyte versionMajor; 276 ubyte versionMinor; 277 ubyte units; 278 ushort xDensity; 279 ushort yDensity; 280 ubyte thumbnailWidth; 281 ubyte thumbnailHeight; 282 ubyte[] thumbnail; 283 284 void free() 285 { 286 if (thumbnail.length) 287 Delete(thumbnail); 288 } 289 } 290 291 struct DQT 292 { 293 ubyte precision; 294 ubyte tableId; 295 ubyte[] table; 296 297 void free() 298 { 299 if (table.length) 300 Delete(table); 301 } 302 } 303 304 struct SOF0Component 305 { 306 ubyte hSubsampling; 307 ubyte vSubsampling; 308 ubyte dqtTableId; 309 } 310 311 struct SOF0 312 { 313 ubyte precision; 314 ushort height; 315 ushort width; 316 ubyte componentsNum; 317 SOF0Component[] components; 318 319 void free() 320 { 321 if (components.length) 322 Delete(components); 323 } 324 } 325 326 struct DHT 327 { 328 ubyte clas; 329 ubyte tableId; 330 Array!HuffmanTableEntry huffmanTable; 331 HuffmanTreeNode* huffmanTree; 332 333 void free() 334 { 335 huffmanTree.free(); 336 Delete(huffmanTree); 337 huffmanTable.free(); 338 } 339 } 340 341 struct SOSComponent 342 { 343 ubyte tableIdDC; 344 ubyte tableIdAC; 345 } 346 347 struct SOS 348 { 349 ubyte componentsNum; 350 SOSComponent[] components; 351 ubyte spectralSelectionStart; 352 ubyte spectralSelectionEnd; 353 ubyte successiveApproximationBitHigh; 354 ubyte successiveApproximationBitLow; 355 356 void free() 357 { 358 if (components.length) 359 Delete(components); 360 } 361 } 362 363 JFIF jfif; 364 DQT[] dqt; 365 SOF0 sof0; 366 DHT[] dht; 367 SOS sos; 368 369 DQT* addDQT() 370 { 371 if (dqt.length > 0) 372 reallocateArray(dqt, dqt.length+1); 373 else 374 dqt = New!(DQT[])(1); 375 return &dqt[$-1]; 376 } 377 378 DHT* addDHT() 379 { 380 if (dht.length > 0) 381 reallocateArray(dht, dht.length+1); 382 else 383 dht = New!(DHT[])(1); 384 return &dht[$-1]; 385 } 386 387 void free() 388 { 389 jfif.free(); 390 foreach(ref t; dqt) t.free(); 391 if (dqt.length) 392 Delete(dqt); 393 sof0.free(); 394 foreach(ref t; dht) t.free(); 395 if (dht.length) 396 Delete(dht); 397 sos.free(); 398 } 399 400 DQT* getQuantizationTable(ubyte id) 401 { 402 foreach(ref t; dqt) 403 if (t.tableId == id) 404 return &t; 405 return null; 406 } 407 408 DHT* getHuffmanTable(ubyte clas, ubyte id) 409 { 410 foreach(ref t; dht) 411 if (t.clas == clas && 412 t.tableId == id) 413 return &t; 414 return null; 415 } 416 } 417 418 /** 419 * Load JPEG from file using local FileSystem. 420 * Causes GC allocation 421 */ 422 SuperImage loadJPEG(string filename) 423 { 424 InputStream input = openForInput(filename); 425 auto img = loadJPEG(input); 426 input.close(); 427 return img; 428 } 429 430 /** 431 * Load JPEG from stream using default image factory. 432 * Causes GC allocation 433 */ 434 SuperImage loadJPEG(InputStream istrm) 435 { 436 Compound!(SuperImage, string) res = 437 loadJPEG(istrm, defaultImageFactory); 438 if (res[0] is null) 439 throw new Exception(res[1]); 440 else 441 return res[0]; 442 } 443 444 /** 445 * Load JPEG from stream using specified image factory. 446 * GC-free 447 */ 448 Compound!(SuperImage, string) loadJPEG( 449 InputStream istrm, 450 SuperImageFactory imgFac) 451 { 452 JPEGImage jpg; 453 SuperImage img = null; 454 455 while (istrm.readable) 456 { 457 JPEGMarkerType mt; 458 auto res = readMarker(&jpg, istrm, &mt); 459 if (res[0]) 460 { 461 // TODO: add progressive JPEG support 462 if (mt == JPEGMarkerType.SOF2) 463 { 464 jpg.free(); 465 return compound(img, "loadJPEG error: progressive JPEG is not supported"); 466 } 467 else if (mt == JPEGMarkerType.SOS) 468 break; 469 } 470 else 471 { 472 jpg.free(); 473 return compound(img, res[1]); 474 } 475 } 476 auto res = decodeScanData(&jpg, istrm, imgFac); 477 jpg.free(); 478 return res; 479 } 480 481 /* 482 * Decode marker from JPEG stream 483 */ 484 Compound!(bool, string) readMarker( 485 JPEGImage* jpg, 486 InputStream istrm, 487 JPEGMarkerType* mt) 488 { 489 ushort magic = istrm.readNumeric!ushort(Endian.Big); 490 491 switch (magic) 492 { 493 case 0xFFD8: 494 *mt = JPEGMarkerType.SOI; 495 version(JPEGDebug) writeln("SOI"); 496 break; 497 498 case 0xFFE0: 499 *mt = JPEGMarkerType.APP0; 500 return readJFIF(jpg, istrm); 501 502 case 0xFFE1: 503 *mt = JPEGMarkerType.APPn; 504 return readAPPn(jpg, istrm, 1); 505 506 case 0xFFE2: 507 *mt = JPEGMarkerType.APPn; 508 return readAPPn(jpg, istrm, 2); 509 510 case 0xFFE3: 511 *mt = JPEGMarkerType.APPn; 512 return readAPPn(jpg, istrm, 3); 513 514 case 0xFFE4: 515 *mt = JPEGMarkerType.APPn; 516 return readAPPn(jpg, istrm, 4); 517 518 case 0xFFE5: 519 *mt = JPEGMarkerType.APPn; 520 return readAPPn(jpg, istrm, 5); 521 522 case 0xFFE6: 523 *mt = JPEGMarkerType.APPn; 524 return readAPPn(jpg, istrm, 6); 525 526 case 0xFFE7: 527 *mt = JPEGMarkerType.APPn; 528 return readAPPn(jpg, istrm, 7); 529 530 case 0xFFE8: 531 *mt = JPEGMarkerType.APPn; 532 return readAPPn(jpg, istrm, 8); 533 534 case 0xFFE9: 535 *mt = JPEGMarkerType.APPn; 536 return readAPPn(jpg, istrm, 9); 537 538 case 0xFFEA: 539 *mt = JPEGMarkerType.APPn; 540 return readAPPn(jpg, istrm, 10); 541 542 case 0xFFEB: 543 *mt = JPEGMarkerType.APPn; 544 return readAPPn(jpg, istrm, 11); 545 546 case 0xFFEC: 547 *mt = JPEGMarkerType.APPn; 548 return readAPPn(jpg, istrm, 12); 549 550 case 0xFFED: 551 *mt = JPEGMarkerType.APPn; 552 return readAPPn(jpg, istrm, 13); 553 554 case 0xFFEE: 555 *mt = JPEGMarkerType.APPn; 556 return readAPPn(jpg, istrm, 14); 557 558 case 0xFFEF: 559 *mt = JPEGMarkerType.APPn; 560 return readAPPn(jpg, istrm, 15); 561 562 case 0xFFDB: 563 *mt = JPEGMarkerType.DQT; 564 return readDQT(jpg, istrm); 565 566 case 0xFFC0: 567 *mt = JPEGMarkerType.SOF0; 568 return readSOF0(jpg, istrm); 569 570 case 0xFFC2: 571 *mt = JPEGMarkerType.SOF2; 572 break; 573 574 case 0xFFC4: 575 *mt = JPEGMarkerType.DHT; 576 return readDHT(jpg, istrm); 577 578 case 0xFFDA: 579 *mt = JPEGMarkerType.SOS; 580 return readSOS(jpg, istrm); 581 582 case 0xFFFE: 583 *mt = JPEGMarkerType.COM; 584 return readCOM(jpg, istrm); 585 586 default: 587 *mt = JPEGMarkerType.Unknown; 588 break; 589 } 590 591 return compound(true, ""); 592 } 593 594 Compound!(bool, string) readJFIF(JPEGImage* jpg, InputStream istrm) 595 { 596 ushort jfif_length = istrm.readNumeric!ushort(Endian.Big); 597 598 char[5] jfif_id = istrm.readChars!5; 599 if (jfif_id != "JFIF\0") 600 return compound(false, "loadJPEG error: illegal JFIF header"); 601 602 jpg.jfif.versionMajor = istrm.readNumeric!ubyte; 603 jpg.jfif.versionMinor = istrm.readNumeric!ubyte; 604 jpg.jfif.units = istrm.readNumeric!ubyte; 605 jpg.jfif.xDensity = istrm.readNumeric!ushort(Endian.Big); 606 jpg.jfif.yDensity = istrm.readNumeric!ushort(Endian.Big); 607 jpg.jfif.thumbnailWidth = istrm.readNumeric!ubyte; 608 jpg.jfif.thumbnailHeight = istrm.readNumeric!ubyte; 609 610 uint jfif_thumb_length = jpg.jfif.thumbnailWidth * jpg.jfif.thumbnailHeight * 3; 611 if (jfif_thumb_length > 0) 612 { 613 jpg.jfif.thumbnail = New!(ubyte[])(jfif_thumb_length); 614 istrm.readBytes(jpg.jfif.thumbnail.ptr, jfif_thumb_length); 615 } 616 617 version(JPEGDebug) 618 { 619 writefln("APP0/JFIF length: %s", jfif_length); 620 writefln("APP0/JFIF identifier: %s", jfif_id); 621 writefln("APP0/JFIF version major: %s", jpg.jfif.versionMajor); 622 writefln("APP0/JFIF version minor: %s", jpg.jfif.versionMinor); 623 writefln("APP0/JFIF units: %s", jpg.jfif.units); 624 writefln("APP0/JFIF xdensity: %s", jpg.jfif.xDensity); 625 writefln("APP0/JFIF ydensity: %s", jpg.jfif.yDensity); 626 writefln("APP0/JFIF xthumbnail: %s", jpg.jfif.thumbnailWidth); 627 writefln("APP0/JFIF ythumbnail: %s", jpg.jfif.thumbnailHeight); 628 } 629 630 return compound(true, ""); 631 } 632 633 /* 634 * APP1 - EXIF, XMP, ExtendedXMP, QVCI, FLIR 635 * APP2 - ICC, FPXR, MPF, PreviewImage 636 * APP3 - Kodak Meta, Stim, PreviewImage 637 * APP4 - Scalado, FPXR, PreviewImage 638 * APP5 - RMETA, PreviewImage 639 * APP6 - EPPIM, NITF, HP TDHD 640 * APP7 - Pentax, Qualcomm 641 * APP8 - SPIFF 642 * APP9 - MediaJukebox 643 * APP10 - PhotoStudio comment 644 * APP11 - JPEG-HDR 645 * APP12 - PictureInfo, Ducky 646 * APP13 - Photoshop, Adobe CM 647 * APP14 - Adobe 648 * APP15 - GraphicConverter 649 */ 650 Compound!(bool, string) readAPPn(JPEGImage* jpg, InputStream istrm, uint n) 651 { 652 ushort app_length = istrm.readNumeric!ushort(Endian.Big); 653 ubyte[] app = New!(ubyte[])(app_length-2); 654 istrm.readBytes(app.ptr, app_length-2); 655 656 // TODO: interpret APP data (EXIF etc.) and save it somewhere. 657 // Maybe add a generic ImageInfo object for this? 658 Delete(app); 659 660 version(JPEGDebug) 661 { 662 writefln("APP%s length: %s", n, app_length); 663 } 664 665 return compound(true, ""); 666 } 667 668 Compound!(bool, string) readCOM(JPEGImage* jpg, InputStream istrm) 669 { 670 ushort com_length = istrm.readNumeric!ushort(Endian.Big); 671 ubyte[] com = New!(ubyte[])(com_length-2); 672 istrm.readBytes(com.ptr, com_length-2); 673 674 version(JPEGDebug) 675 { 676 writefln("COM string: \"%s\"", cast(string)com); 677 writefln("COM length: %s", com_length); 678 } 679 680 // TODO: save COM data somewhere. 681 // Maybe add a generic ImageInfo object for this? 682 Delete(com); 683 684 return compound(true, ""); 685 } 686 687 Compound!(bool, string) readDQT(JPEGImage* jpg, InputStream istrm) 688 { 689 ushort dqt_length = istrm.readNumeric!ushort(Endian.Big); 690 version(JPEGDebug) 691 { 692 writefln("DQT length: %s", dqt_length); 693 } 694 695 dqt_length -= 2; 696 697 while(dqt_length) 698 { 699 JPEGImage.DQT* dqt = jpg.addDQT(); 700 701 ubyte bite = istrm.readNumeric!ubyte; 702 dqt.precision = bite.hiNibble; 703 dqt.tableId = bite.loNibble; 704 705 dqt_length--; 706 707 if (dqt.precision == 0) 708 { 709 dqt.table = New!(ubyte[])(64); 710 dqt_length -= 64; 711 } 712 else if (dqt.precision == 1) 713 { 714 dqt.table = New!(ubyte[])(128); 715 dqt_length -= 128; 716 } 717 718 istrm.readBytes(dqt.table.ptr, dqt.table.length); 719 720 version(JPEGDebug) 721 { 722 writefln("DQT precision: %s", dqt.precision); 723 writefln("DQT table id: %s", dqt.tableId); 724 writefln("DQT table: %s", dqt.table); 725 } 726 } 727 728 return compound(true, ""); 729 } 730 731 Compound!(bool, string) readSOF0(JPEGImage* jpg, InputStream istrm) 732 { 733 ushort sof0_length = istrm.readNumeric!ushort(Endian.Big); 734 jpg.sof0.precision = istrm.readNumeric!ubyte; 735 jpg.sof0.height = istrm.readNumeric!ushort(Endian.Big); 736 jpg.sof0.width = istrm.readNumeric!ushort(Endian.Big); 737 jpg.sof0.componentsNum = istrm.readNumeric!ubyte; 738 739 version(JPEGDebug) 740 { 741 writefln("SOF0 length: %s", sof0_length); 742 writefln("SOF0 precision: %s", jpg.sof0.precision); 743 writefln("SOF0 height: %s", jpg.sof0.height); 744 writefln("SOF0 width: %s", jpg.sof0.width); 745 writefln("SOF0 components: %s", jpg.sof0.componentsNum); 746 } 747 748 jpg.sof0.components = New!(JPEGImage.SOF0Component[])(jpg.sof0.componentsNum); 749 750 foreach(ref c; jpg.sof0.components) 751 { 752 ubyte c_id = istrm.readNumeric!ubyte; 753 ubyte bite = istrm.readNumeric!ubyte; 754 c.hSubsampling = bite.hiNibble; 755 c.vSubsampling = bite.loNibble; 756 c.dqtTableId = istrm.readNumeric!ubyte; 757 version(JPEGDebug) 758 { 759 writefln("SOF0 component id: %s", c_id); 760 writefln("SOF0 component %s hsubsampling: %s", c_id, c.hSubsampling); 761 writefln("SOF0 component %s vsubsampling: %s", c_id, c.vSubsampling); 762 writefln("SOF0 component %s table id: %s", c_id, c.dqtTableId); 763 } 764 } 765 766 return compound(true, ""); 767 } 768 769 Compound!(bool, string) readDHT(JPEGImage* jpg, InputStream istrm) 770 { 771 ushort dht_length = istrm.readNumeric!ushort(Endian.Big); 772 version(JPEGDebug) 773 { 774 writefln("DHT length: %s", dht_length); 775 } 776 777 dht_length -= 2; 778 779 while(dht_length > 0) 780 { 781 JPEGImage.DHT* dht = jpg.addDHT(); 782 783 ubyte bite = istrm.readNumeric!ubyte; 784 dht_length--; 785 dht.clas = bite.hiNibble; 786 dht.tableId = bite.loNibble; 787 788 ubyte[16] dht_code_lengths; 789 istrm.readBytes(dht_code_lengths.ptr, 16); 790 dht_length -= 16; 791 792 version(JPEGDebug) 793 { 794 writefln("DHT class: %s (%s)", 795 dht.clas, 796 dht.clas? "AC":"DC"); 797 writefln("DHT tableId: %s", dht.tableId); 798 writefln("DHT Huffman code lengths: %s", dht_code_lengths); 799 } 800 801 // Read Huffman table 802 int totalCodes = reduce!("a + b")(0, dht_code_lengths); 803 int storedCodes = 0; 804 ubyte treeLevel = 0; 805 ushort bits = 0; 806 807 while (storedCodes != totalCodes) 808 { 809 while (treeLevel < 15 && 810 dht_code_lengths[treeLevel] == 0) 811 { 812 treeLevel++; 813 bits *= 2; 814 } 815 816 if (treeLevel < 16) 817 { 818 uint bitsNum = treeLevel + 1; 819 HuffmanCode code = HuffmanCode(bits, cast(ushort)bitsNum); 820 821 auto entry = HuffmanTableEntry(code, istrm.readNumeric!ubyte); 822 dht.huffmanTable.append(entry); 823 824 dht_length--; 825 826 storedCodes++; 827 bits++; 828 dht_code_lengths[treeLevel]--; 829 } 830 } 831 832 dht.huffmanTree = treeFromTable(dht.huffmanTable); 833 } 834 835 return compound(true, ""); 836 } 837 838 Compound!(bool, string) readSOS(JPEGImage* jpg, InputStream istrm) 839 { 840 ushort sos_length = istrm.readNumeric!ushort(Endian.Big); 841 jpg.sos.componentsNum = istrm.readNumeric!ubyte; 842 843 version(JPEGDebug) 844 { 845 writefln("SOS length: %s", sos_length); 846 writefln("SOS components: %s", jpg.sos.componentsNum); 847 } 848 849 jpg.sos.components = New!(JPEGImage.SOSComponent[])(jpg.sos.componentsNum); 850 851 foreach(ref c; jpg.sos.components) 852 { 853 ubyte c_id = istrm.readNumeric!ubyte; 854 ubyte bite = istrm.readNumeric!ubyte; 855 c.tableIdDC = bite.hiNibble; 856 c.tableIdAC = bite.loNibble; 857 version(JPEGDebug) 858 { 859 writefln("SOS component id: %s", c_id); 860 writefln("SOS component %s DC table id: %s", c_id, c.tableIdDC); 861 writefln("SOS component %s AC table id: %s", c_id, c.tableIdAC); 862 } 863 } 864 865 jpg.sos.spectralSelectionStart = istrm.readNumeric!ubyte; 866 jpg.sos.spectralSelectionEnd = istrm.readNumeric!ubyte; 867 ubyte bite = istrm.readNumeric!ubyte; 868 jpg.sos.successiveApproximationBitHigh = bite.hiNibble; 869 jpg.sos.successiveApproximationBitLow = bite.loNibble; 870 871 version(JPEGDebug) 872 { 873 writefln("SOS spectral selection start: %s", jpg.sos.spectralSelectionStart); 874 writefln("SOS spectral selection end: %s", jpg.sos.spectralSelectionEnd); 875 writefln("SOS successive approximation bit: %s", jpg.sos.successiveApproximationBitHigh); 876 writefln("SOS successive approximation bit low: %s", jpg.sos.successiveApproximationBitLow); 877 } 878 879 return compound(true, ""); 880 } 881 882 struct ScanBitStream 883 { 884 InputStream istrm; 885 886 bool endMarkerFound = false; 887 uint bytesRead = 0; 888 ubyte prevByte = 0x00; 889 ubyte curByte = 0x00; 890 891 ubyte readNextByte() 892 { 893 ubyte b = istrm.readNumeric!ubyte; 894 bytesRead++; 895 endMarkerFound = (prevByte == 0xFF && b == 0xD9); 896 assert(!endMarkerFound); 897 if (!endMarkerFound) 898 { 899 prevByte = b; 900 curByte = b; 901 return b; 902 } 903 else 904 { 905 curByte = 0; 906 } 907 return curByte; 908 } 909 910 bool readable() 911 { 912 return !istrm.readable || endMarkerFound; 913 } 914 915 uint bitPos = 0; 916 917 // Huffman decode a byte 918 Compound!(bool, string) decodeByte(HuffmanTreeNode* node, ubyte* result) 919 { 920 while(!node.isLeaf) 921 { 922 ubyte b = curByte; 923 924 bool bit = getBit(b, 7-bitPos); 925 bitPos++; 926 if (bitPos == 8) 927 { 928 bitPos = 0; 929 readNextByte(); 930 931 if (b == 0xFF) 932 { 933 b = curByte; 934 if (b == 0x00) 935 { 936 readNextByte(); 937 } 938 } 939 } 940 941 if (bit) 942 node = node.right; 943 else 944 node = node.left; 945 946 if (node is null) 947 return compound(false, "loadJPEG error: no Huffman code found"); 948 } 949 950 *result = node.ch; 951 return compound(true, ""); 952 } 953 954 // Read len bits from stream to buffer 955 uint readBits(ubyte len) 956 { 957 uint buffer = 0; 958 uint i = 0; 959 uint by = 0; 960 uint bi = 0; 961 962 while (i < len) 963 { 964 ubyte b = curByte; 965 966 bool bit = getBit(b, 7-bitPos); 967 buffer = setBit(buffer, (by * 8 + bi), bit); 968 969 bi++; 970 if (bi == 8) 971 { 972 bi = 0; 973 by++; 974 } 975 976 i++; 977 978 bitPos++; 979 if (bitPos == 8) 980 { 981 bitPos = 0; 982 readNextByte(); 983 984 if (b == 0xFF) 985 { 986 b = curByte; 987 if (b == 0x00) 988 readNextByte(); 989 } 990 } 991 } 992 993 return buffer; 994 } 995 } 996 997 /* 998 * Decodes compressed data and creates RGB image from it 999 */ 1000 Compound!(SuperImage, string) decodeScanData( 1001 JPEGImage* jpg, 1002 InputStream istrm, 1003 SuperImageFactory imgFac) 1004 { 1005 SuperImage img = imgFac.createImage(jpg.sof0.width, jpg.sof0.height, 3, 8); 1006 1007 MCU mcu; 1008 foreach(ci, ref c; jpg.sof0.components) 1009 { 1010 if (ci == 0) 1011 mcu.createYBlocks(c.hSubsampling, c.vSubsampling); 1012 else if (ci == 1) 1013 mcu.createCbBlocks(c.hSubsampling, c.vSubsampling); 1014 else if (ci == 2) 1015 mcu.createCrBlocks(c.hSubsampling, c.vSubsampling); 1016 } 1017 1018 Compound!(SuperImage, string) error(string errorMsg) 1019 { 1020 mcu.free(); 1021 if (img) 1022 { 1023 img.free(); 1024 img = null; 1025 } 1026 return compound(img, errorMsg); 1027 } 1028 1029 // Decode DCT coefficient from bit buffer 1030 int decodeCoef(uint buffer, ubyte numBits) 1031 { 1032 bool positive = getBit(buffer, 0); 1033 1034 int value = 0; 1035 foreach(j; 0..numBits) 1036 { 1037 bool bit = getBit(buffer, numBits-1-j); 1038 value = setBit(value, j, bit); 1039 } 1040 1041 if (positive) 1042 return value; 1043 else 1044 return value - 2^^numBits + 1; 1045 } 1046 1047 static const ubyte[64] dezigzag = 1048 [ 1049 0, 1, 8, 16, 9, 2, 3, 10, 1050 17, 24, 32, 25, 18, 11, 4, 5, 1051 12, 19, 26, 33, 40, 48, 41, 34, 1052 27, 20, 13, 6, 7, 14, 21, 28, 1053 35, 42, 49, 56, 57, 50, 43, 36, 1054 29, 22, 15, 23, 30, 37, 44, 51, 1055 58, 59, 52, 45, 38, 31, 39, 46, 1056 53, 60, 61, 54, 47, 55, 62, 63 1057 ]; 1058 1059 if (jpg.sos.componentsNum != 3) 1060 { 1061 return error(format( 1062 "loadJPEG error: unsupported number of components: %s", 1063 jpg.sos.componentsNum)); 1064 } 1065 1066 // Store previous DC coefficients 1067 int[3] dcCoefPrev; 1068 1069 if (jpg.dqt.length == 0) 1070 return error("loadJPEG error: no DQTs found"); 1071 1072 ScanBitStream sbs; 1073 sbs.endMarkerFound = false; 1074 sbs.bytesRead = 0; 1075 sbs.prevByte = 0x00; 1076 sbs.curByte = 0x00; 1077 sbs.istrm = istrm; 1078 sbs.readNextByte(); 1079 1080 uint numMCUsH = jpg.sof0.width / mcu.width + ((jpg.sof0.width % mcu.width) > 0); 1081 uint numMCUsV = jpg.sof0.height / mcu.height + ((jpg.sof0.height % mcu.height) > 0); 1082 1083 // Read MCUs 1084 foreach(mcuY; 0..numMCUsV) 1085 foreach(mcuX; 0..numMCUsH) 1086 { 1087 // Read MCU for each channel 1088 foreach(ci, ref c; jpg.sos.components) 1089 { 1090 auto tableDC = jpg.getHuffmanTable(0, c.tableIdDC); 1091 auto tableAC = jpg.getHuffmanTable(1, c.tableIdAC); 1092 1093 if (tableDC is null) 1094 return error("loadJPEG error: illegal DC table index in MCU component"); 1095 if (tableAC is null) 1096 return error("loadJPEG error: illegal AC table index in MCU component"); 1097 1098 auto component = jpg.sof0.components[ci]; 1099 auto hblocks = component.hSubsampling; 1100 auto vblocks = component.vSubsampling; 1101 auto dqtTableId = component.dqtTableId; 1102 if (dqtTableId >= jpg.dqt.length) 1103 return error("loadJPEG error: illegal DQT table index in MCU component"); 1104 1105 // Read 8x8 blocks 1106 foreach(by; 0..vblocks) 1107 foreach(bx; 0..hblocks) 1108 { 1109 int[8*8] block; 1110 1111 // Read DC coefficient 1112 ubyte dcDiffLen; 1113 auto res = sbs.decodeByte(tableDC.huffmanTree, &dcDiffLen); 1114 if (!res[0]) return error(res[1]); 1115 1116 if (dcDiffLen > 0) 1117 { 1118 uint dcBuffer = sbs.readBits(dcDiffLen); 1119 dcCoefPrev[ci] += decodeCoef(dcBuffer, dcDiffLen); 1120 } 1121 1122 block[0] = dcCoefPrev[ci]; 1123 1124 // Read AC coefficients 1125 { 1126 uint i = 1; 1127 bool eob = false; 1128 while (!eob && i < 64) 1129 { 1130 ubyte code; 1131 res = sbs.decodeByte(tableAC.huffmanTree, &code); 1132 if (!res[0]) return error(res[1]); 1133 1134 if (code == 0x00) // EOB, all next values are zero 1135 eob = true; 1136 else if (code == 0xF0) // ZRL, next 16 values are zero 1137 { 1138 foreach(j; 0..16) 1139 if (i < 64) 1140 { 1141 block[i] = 0; 1142 i++; 1143 } 1144 } 1145 else 1146 { 1147 ubyte hi = hiNibble(code); 1148 ubyte lo = loNibble(code); 1149 1150 uint zeroes = hi; 1151 foreach(j; 0..zeroes) 1152 if (i < 64) 1153 { 1154 block[i] = 0; 1155 i++; 1156 } 1157 1158 int acCoef = 0; 1159 if (lo > 0) 1160 { 1161 uint acBuffer = sbs.readBits(lo); 1162 acCoef = decodeCoef(acBuffer, lo); 1163 } 1164 1165 if (i < 64) 1166 block[i] = acCoef; 1167 1168 i++; 1169 } 1170 } 1171 } 1172 1173 // Multiply block by quantization matrix 1174 foreach(i, ref v; block) 1175 v *= jpg.dqt[dqtTableId].table[i]; 1176 1177 // Convert matrix from zig-zag order to normal order 1178 int[8*8] dctMatrix; 1179 1180 foreach(i, v; block) 1181 dctMatrix[dezigzag[i]] = v; 1182 1183 idct64(dctMatrix.ptr); 1184 1185 // Copy the matrix into corresponding channel 1186 int* outMatrixPtr; 1187 if (ci == 0) 1188 outMatrixPtr = mcu.yBlocks[by * hblocks + bx].ptr; 1189 else if (ci == 1) 1190 outMatrixPtr = mcu.cbBlocks[by * hblocks + bx].ptr; 1191 else if (ci == 2) 1192 outMatrixPtr = mcu.crBlocks[by * hblocks + bx].ptr; 1193 else 1194 return error("loadJPEG error: illegal component index"); 1195 1196 for(uint i = 0; i < 64; i++) 1197 outMatrixPtr[i] = dctMatrix[i]; 1198 } 1199 } 1200 1201 // Convert MCU from YCbCr to RGB 1202 foreach(y; 0..mcu.height) // Pixel coordinates in MCU 1203 foreach(x; 0..mcu.width) 1204 { 1205 Color4f col = mcu.getPixel(x, y); 1206 1207 // Pixel coordinates in image 1208 uint ix = mcuX * mcu.width + x; 1209 uint iy = mcuY * mcu.height + y; 1210 1211 if (ix < img.width && iy < img.height) 1212 img[ix, iy] = col; 1213 } 1214 } 1215 1216 version(JPEGDebug) 1217 { 1218 writefln("Bytes read: %s", sbs.bytesRead); 1219 } 1220 1221 mcu.free(); 1222 1223 return compound(img, ""); 1224 } 1225 1226 /* 1227 * MCU struct keeps a storage for one Minimal Code Unit 1228 * and provides a generalized interface for decoding 1229 * images with different subsampling modes. 1230 * Decoder should read 8x8 blocks one by one for each channel 1231 * and fill corresponding arrays in MCU. 1232 */ 1233 struct MCU 1234 { 1235 uint width; 1236 uint height; 1237 1238 alias Block = int[8*8]; 1239 Block[] yBlocks; 1240 Block[] cbBlocks; 1241 Block[] crBlocks; 1242 1243 uint ySamplesH, ySamplesV; 1244 uint cbSamplesH, cbSamplesV; 1245 uint crSamplesH, crSamplesV; 1246 1247 uint yWidth, yHeight; 1248 uint cbWidth, cbHeight; 1249 uint crWidth, crHeight; 1250 1251 void createYBlocks(uint hsubsampling, uint vsubsampling) 1252 { 1253 yBlocks = New!(Block[])(hsubsampling * vsubsampling); 1254 1255 width = hsubsampling * 8; 1256 height = vsubsampling * 8; 1257 1258 ySamplesH = hsubsampling; 1259 ySamplesV = vsubsampling; 1260 1261 yWidth = width / ySamplesH; 1262 yHeight = height / ySamplesV; 1263 } 1264 1265 void createCbBlocks(uint hsubsampling, uint vsubsampling) 1266 { 1267 cbBlocks = New!(Block[])(hsubsampling * vsubsampling); 1268 1269 cbSamplesH = hsubsampling; 1270 cbSamplesV = vsubsampling; 1271 1272 cbWidth = width / cbSamplesH; 1273 cbHeight = height / cbSamplesV; 1274 } 1275 1276 void createCrBlocks(uint hsubsampling, uint vsubsampling) 1277 { 1278 crBlocks = New!(Block[])(hsubsampling * vsubsampling); 1279 1280 crSamplesH = hsubsampling; 1281 crSamplesV = vsubsampling; 1282 1283 crWidth = width / crSamplesH; 1284 crHeight = height / crSamplesV; 1285 } 1286 1287 void free() 1288 { 1289 if (yBlocks.length) Delete(yBlocks); 1290 if (cbBlocks.length) Delete(cbBlocks); 1291 if (crBlocks.length) Delete(crBlocks); 1292 } 1293 1294 Color4f getPixel(uint x, uint y) // coordinates relative to upper-left MCU corner 1295 { 1296 // Y block coordinates 1297 uint ybx = x / yWidth; 1298 uint yby = y / yHeight; 1299 uint ybi = yby * ySamplesH + ybx; 1300 1301 // Pixel coordinates in Y block 1302 uint ybpx = x - ybx * yWidth; 1303 uint ybpy = y - yby * yHeight; 1304 1305 // Cb block coordinates 1306 uint cbx = x / cbWidth; 1307 uint cby = y / cbHeight; 1308 uint cbi = cby * cbSamplesH + cbx; 1309 1310 // Pixel coordinates in Cb block 1311 uint cbpx = (x - cbx * cbWidth) / ySamplesH; 1312 uint cbpy = (y - cby * cbHeight) / ySamplesV; 1313 1314 // Cr block coordinates 1315 uint crx = x / crWidth; 1316 uint cry = y / crHeight; 1317 uint cri = cry * crSamplesH + crx; 1318 1319 // Pixel coordinates in Cr block 1320 uint crpx = (x - crx * crWidth) / ySamplesH; 1321 uint crpy = (y - cry * crHeight) / ySamplesV; 1322 1323 // Get color components 1324 float Y = cast(float)yBlocks [ybi][ybpy * 8 + ybpx] + 128.0f; 1325 float Cb = cast(float)cbBlocks[cbi][cbpy * 8 + cbpx]; 1326 float Cr = cast(float)crBlocks[cri][crpy * 8 + crpx]; 1327 1328 // Convert from YCbCr to RGB 1329 Color4f col; 1330 col.r = Y + 1.402f * Cr; 1331 col.g = Y - 0.34414f * Cb - 0.71414f * Cr; 1332 col.b = Y + 1.772f * Cb; 1333 col = col / 255.0f; 1334 col.a = 1.0f; 1335 1336 return col; 1337 } 1338 } 1339 /* 1340 * Inverse discrete cosine transform (DCT) for 64x64 blocks 1341 * 1342 * The input coefficients should already have been multiplied by the 1343 * appropriate quantization table. We use fixed-point computation, with the 1344 * number of bits for the fractional component varying over the intermediate 1345 * stages. 1346 * 1347 * For more on the actual algorithm, see Z. Wang, "Fast algorithms for the 1348 * discrete W transform and for the discrete Fourier transform", IEEE Trans. on 1349 * ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984. 1350 */ 1351 void idct64(int* src) 1352 { 1353 enum blockSize = 64; // A DCT block is 8x8. 1354 1355 enum w1 = 2841; // 2048*sqrt(2)*cos(1*pi/16) 1356 enum w2 = 2676; // 2048*sqrt(2)*cos(2*pi/16) 1357 enum w3 = 2408; // 2048*sqrt(2)*cos(3*pi/16) 1358 enum w5 = 1609; // 2048*sqrt(2)*cos(5*pi/16) 1359 enum w6 = 1108; // 2048*sqrt(2)*cos(6*pi/16) 1360 enum w7 = 565; // 2048*sqrt(2)*cos(7*pi/16) 1361 1362 enum w1pw7 = w1 + w7; 1363 enum w1mw7 = w1 - w7; 1364 enum w2pw6 = w2 + w6; 1365 enum w2mw6 = w2 - w6; 1366 enum w3pw5 = w3 + w5; 1367 enum w3mw5 = w3 - w5; 1368 1369 enum r2 = 181; // 256/sqrt(2) 1370 1371 // Horizontal 1-D IDCT. 1372 for (uint y = 0; y < 8; y++) 1373 { 1374 int y8 = y * 8; 1375 // If all the AC components are zero, then the IDCT is trivial. 1376 if (src[y8+1] == 0 && src[y8+2] == 0 && src[y8+3] == 0 && 1377 src[y8+4] == 0 && src[y8+5] == 0 && src[y8+6] == 0 && src[y8+7] == 0) 1378 { 1379 int dc = src[y8+0] << 3; 1380 src[y8+0] = dc; 1381 src[y8+1] = dc; 1382 src[y8+2] = dc; 1383 src[y8+3] = dc; 1384 src[y8+4] = dc; 1385 src[y8+5] = dc; 1386 src[y8+6] = dc; 1387 src[y8+7] = dc; 1388 continue; 1389 } 1390 1391 // Prescale. 1392 int x0 = (src[y8+0] << 11) + 128; 1393 int x1 = src[y8+4] << 11; 1394 int x2 = src[y8+6]; 1395 int x3 = src[y8+2]; 1396 int x4 = src[y8+1]; 1397 int x5 = src[y8+7]; 1398 int x6 = src[y8+5]; 1399 int x7 = src[y8+3]; 1400 1401 // Stage 1. 1402 int x8 = w7 * (x4 + x5); 1403 x4 = x8 + w1mw7*x4; 1404 x5 = x8 - w1pw7*x5; 1405 x8 = w3 * (x6 + x7); 1406 x6 = x8 - w3mw5*x6; 1407 x7 = x8 - w3pw5*x7; 1408 1409 // Stage 2. 1410 x8 = x0 + x1; 1411 x0 -= x1; 1412 x1 = w6 * (x3 + x2); 1413 x2 = x1 - w2pw6*x2; 1414 x3 = x1 + w2mw6*x3; 1415 x1 = x4 + x6; 1416 x4 -= x6; 1417 x6 = x5 + x7; 1418 x5 -= x7; 1419 1420 // Stage 3. 1421 x7 = x8 + x3; 1422 x8 -= x3; 1423 x3 = x0 + x2; 1424 x0 -= x2; 1425 x2 = (r2*(x4+x5) + 128) >> 8; 1426 x4 = (r2*(x4-x5) + 128) >> 8; 1427 1428 // Stage 4. 1429 src[y8+0] = (x7 + x1) >> 8; 1430 src[y8+1] = (x3 + x2) >> 8; 1431 src[y8+2] = (x0 + x4) >> 8; 1432 src[y8+3] = (x8 + x6) >> 8; 1433 src[y8+4] = (x8 - x6) >> 8; 1434 src[y8+5] = (x0 - x4) >> 8; 1435 src[y8+6] = (x3 - x2) >> 8; 1436 src[y8+7] = (x7 - x1) >> 8; 1437 } 1438 1439 // Vertical 1-D IDCT. 1440 for (uint x = 0; x < 8; x++) 1441 { 1442 // Similar to the horizontal 1-D IDCT case, if all the AC components are zero, then the IDCT is trivial. 1443 // However, after performing the horizontal 1-D IDCT, there are typically non-zero AC components, so 1444 // we do not bother to check for the all-zero case. 1445 1446 // Prescale. 1447 int y0 = (src[8*0+x] << 8) + 8192; 1448 int y1 = src[8*4+x] << 8; 1449 int y2 = src[8*6+x]; 1450 int y3 = src[8*2+x]; 1451 int y4 = src[8*1+x]; 1452 int y5 = src[8*7+x]; 1453 int y6 = src[8*5+x]; 1454 int y7 = src[8*3+x]; 1455 1456 // Stage 1. 1457 int y8 = w7*(y4+y5) + 4; 1458 y4 = (y8 + w1mw7*y4) >> 3; 1459 y5 = (y8 - w1pw7*y5) >> 3; 1460 y8 = w3*(y6+y7) + 4; 1461 y6 = (y8 - w3mw5*y6) >> 3; 1462 y7 = (y8 - w3pw5*y7) >> 3; 1463 1464 // Stage 2. 1465 y8 = y0 + y1; 1466 y0 -= y1; 1467 y1 = w6*(y3+y2) + 4; 1468 y2 = (y1 - w2pw6*y2) >> 3; 1469 y3 = (y1 + w2mw6*y3) >> 3; 1470 y1 = y4 + y6; 1471 y4 -= y6; 1472 y6 = y5 + y7; 1473 y5 -= y7; 1474 1475 // Stage 3. 1476 y7 = y8 + y3; 1477 y8 -= y3; 1478 y3 = y0 + y2; 1479 y0 -= y2; 1480 y2 = (r2*(y4+y5) + 128) >> 8; 1481 y4 = (r2*(y4-y5) + 128) >> 8; 1482 1483 // Stage 4. 1484 src[8*0+x] = (y7 + y1) >> 14; 1485 src[8*1+x] = (y3 + y2) >> 14; 1486 src[8*2+x] = (y0 + y4) >> 14; 1487 src[8*3+x] = (y8 + y6) >> 14; 1488 src[8*4+x] = (y8 - y6) >> 14; 1489 src[8*5+x] = (y0 - y4) >> 14; 1490 src[8*6+x] = (y3 - y2) >> 14; 1491 src[8*7+x] = (y7 - y1) >> 14; 1492 } 1493 }