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