-
Notifications
You must be signed in to change notification settings - Fork 128
/
jpeg.d
4578 lines (3894 loc) · 157 KB
/
jpeg.d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// jpgd.h - C++ class for JPEG decompression.
// Rich Geldreich <richgel99@gmail.com>
// Alex Evans: Linear memory allocator (taken from jpge.h).
// v1.04, May. 19, 2012: Code tweaks to fix VS2008 static code analysis warnings (all looked harmless)
// D translation by Ketmar // Invisible Vector
//
// This is free and unencumbered software released into the public domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.
//
// In jurisdictions that recognize copyright laws, the author or authors
// of this software dedicate any and all copyright interest in the
// software to the public domain. We make this dedication for the benefit
// of the public at large and to the detriment of our heirs and
// successors. We intend this dedication to be an overt act of
// relinquishment in perpetuity of all present and future rights to this
// software under copyright law.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// For more information, please refer to <http://unlicense.org/>
//
// Supports progressive and baseline sequential JPEG image files, and the most common chroma subsampling factors: Y, H1V1, H2V1, H1V2, and H2V2.
//
// Chroma upsampling quality: H2V2 is upsampled in the frequency domain, H2V1 and H1V2 are upsampled using point sampling.
// Chroma upsampling reference: "Fast Scheme for Image Size Change in the Compressed Domain"
// http://vision.ai.uiuc.edu/~dugad/research/dct/index.html
/**
* Loads a JPEG image from a memory buffer or a file.
*
* req_comps can be 1 (grayscale), 3 (RGB), or 4 (RGBA).
* On return, width/height will be set to the image's dimensions, and actual_comps will be set to the either 1 (grayscale) or 3 (RGB).
* Requesting a 8 or 32bpp image is currently a little faster than 24bpp because the jpeg_decoder class itself currently always unpacks to either 8 or 32bpp.
*/
module arsd.jpeg;
@system:
// Set to 1 to enable freq. domain chroma upsampling on images using H2V2 subsampling (0=faster nearest neighbor sampling).
// This is slower, but results in higher quality on images with highly saturated colors.
version = JPGD_SUPPORT_FREQ_DOMAIN_UPSAMPLING;
/// Input stream interface.
/// This delegate is called when the internal input buffer is empty.
/// Parameters:
/// pBuf - input buffer
/// max_bytes_to_read - maximum bytes that can be written to pBuf
/// pEOF_flag - set this to true if at end of stream (no more bytes remaining)
/// Returns -1 on error, otherwise return the number of bytes actually written to the buffer (which may be 0).
/// Notes: This delegate will be called in a loop until you set *pEOF_flag to true or the internal buffer is full.
alias JpegStreamReadFunc = int delegate (void* pBuf, int max_bytes_to_read, bool* pEOF_flag);
// ////////////////////////////////////////////////////////////////////////// //
private:
void *jpgd_malloc (size_t nSize) { import core.stdc.stdlib : malloc; return malloc(nSize); }
void jpgd_free (void *p) { import core.stdc.stdlib : free; if (p !is null) free(p); }
// Success/failure error codes.
alias jpgd_status = int;
enum /*jpgd_status*/ {
JPGD_SUCCESS = 0, JPGD_FAILED = -1, JPGD_DONE = 1,
JPGD_BAD_DHT_COUNTS = -256, JPGD_BAD_DHT_INDEX, JPGD_BAD_DHT_MARKER, JPGD_BAD_DQT_MARKER, JPGD_BAD_DQT_TABLE,
JPGD_BAD_PRECISION, JPGD_BAD_HEIGHT, JPGD_BAD_WIDTH, JPGD_TOO_MANY_COMPONENTS,
JPGD_BAD_SOF_LENGTH, JPGD_BAD_VARIABLE_MARKER, JPGD_BAD_DRI_LENGTH, JPGD_BAD_SOS_LENGTH,
JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMITIC_SUPPORT, JPGD_UNEXPECTED_MARKER,
JPGD_NOT_JPEG, JPGD_UNSUPPORTED_MARKER, JPGD_BAD_DQT_LENGTH, JPGD_TOO_MANY_BLOCKS,
JPGD_UNDEFINED_QUANT_TABLE, JPGD_UNDEFINED_HUFF_TABLE, JPGD_NOT_SINGLE_SCAN, JPGD_UNSUPPORTED_COLORSPACE,
JPGD_UNSUPPORTED_SAMP_FACTORS, JPGD_DECODE_ERROR, JPGD_BAD_RESTART_MARKER, JPGD_ASSERTION_ERROR,
JPGD_BAD_SOS_SPECTRAL, JPGD_BAD_SOS_SUCCESSIVE, JPGD_STREAM_READ, JPGD_NOTENOUGHMEM,
}
enum {
JPGD_IN_BUF_SIZE = 8192, JPGD_MAX_BLOCKS_PER_MCU = 10, JPGD_MAX_HUFF_TABLES = 8, JPGD_MAX_QUANT_TABLES = 4,
JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 8192, JPGD_MAX_HEIGHT = 16384, JPGD_MAX_WIDTH = 16384,
}
// DCT coefficients are stored in this sequence.
static immutable int[64] g_ZAG = [ 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 ];
alias JPEG_MARKER = int;
enum /*JPEG_MARKER*/ {
M_SOF0 = 0xC0, M_SOF1 = 0xC1, M_SOF2 = 0xC2, M_SOF3 = 0xC3, M_SOF5 = 0xC5, M_SOF6 = 0xC6, M_SOF7 = 0xC7, M_JPG = 0xC8,
M_SOF9 = 0xC9, M_SOF10 = 0xCA, M_SOF11 = 0xCB, M_SOF13 = 0xCD, M_SOF14 = 0xCE, M_SOF15 = 0xCF, M_DHT = 0xC4, M_DAC = 0xCC,
M_RST0 = 0xD0, M_RST1 = 0xD1, M_RST2 = 0xD2, M_RST3 = 0xD3, M_RST4 = 0xD4, M_RST5 = 0xD5, M_RST6 = 0xD6, M_RST7 = 0xD7,
M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_DNL = 0xDC, M_DRI = 0xDD, M_DHP = 0xDE, M_EXP = 0xDF,
M_APP0 = 0xE0, M_APP15 = 0xEF, M_JPG0 = 0xF0, M_JPG13 = 0xFD, M_COM = 0xFE, M_TEM = 0x01, M_ERROR = 0x100, RST0 = 0xD0,
}
alias JPEG_SUBSAMPLING = int;
enum /*JPEG_SUBSAMPLING*/ { JPGD_GRAYSCALE = 0, JPGD_YH1V1, JPGD_YH2V1, JPGD_YH1V2, JPGD_YH2V2 }
enum CONST_BITS = 13;
enum PASS1_BITS = 2;
enum SCALEDONE = cast(int)1;
enum FIX_0_298631336 = cast(int)2446; /* FIX(0.298631336) */
enum FIX_0_390180644 = cast(int)3196; /* FIX(0.390180644) */
enum FIX_0_541196100 = cast(int)4433; /* FIX(0.541196100) */
enum FIX_0_765366865 = cast(int)6270; /* FIX(0.765366865) */
enum FIX_0_899976223 = cast(int)7373; /* FIX(0.899976223) */
enum FIX_1_175875602 = cast(int)9633; /* FIX(1.175875602) */
enum FIX_1_501321110 = cast(int)12299; /* FIX(1.501321110) */
enum FIX_1_847759065 = cast(int)15137; /* FIX(1.847759065) */
enum FIX_1_961570560 = cast(int)16069; /* FIX(1.961570560) */
enum FIX_2_053119869 = cast(int)16819; /* FIX(2.053119869) */
enum FIX_2_562915447 = cast(int)20995; /* FIX(2.562915447) */
enum FIX_3_072711026 = cast(int)25172; /* FIX(3.072711026) */
int DESCALE() (int x, int n) { pragma(inline, true); return (((x) + (SCALEDONE << ((n)-1))) >> (n)); }
int DESCALE_ZEROSHIFT() (int x, int n) { pragma(inline, true); return (((x) + (128 << (n)) + (SCALEDONE << ((n)-1))) >> (n)); }
ubyte CLAMP() (int i) { pragma(inline, true); return cast(ubyte)(cast(uint)i > 255 ? (((~i) >> 31) & 0xFF) : i); }
// Compiler creates a fast path 1D IDCT for X non-zero columns
struct Row(int NONZERO_COLS) {
pure nothrow @trusted @nogc:
static void idct(int* pTemp, const(jpeg_decoder.jpgd_block_t)* pSrc) {
static if (NONZERO_COLS == 0) {
// nothing
} else static if (NONZERO_COLS == 1) {
immutable int dcval = (pSrc[0] << PASS1_BITS);
pTemp[0] = dcval;
pTemp[1] = dcval;
pTemp[2] = dcval;
pTemp[3] = dcval;
pTemp[4] = dcval;
pTemp[5] = dcval;
pTemp[6] = dcval;
pTemp[7] = dcval;
} else {
// ACCESS_COL() will be optimized at compile time to either an array access, or 0.
//#define ACCESS_COL(x) (((x) < NONZERO_COLS) ? (int)pSrc[x] : 0)
template ACCESS_COL(int x) {
static if (x < NONZERO_COLS) enum ACCESS_COL = "cast(int)pSrc["~x.stringof~"]"; else enum ACCESS_COL = "0";
}
immutable int z2 = mixin(ACCESS_COL!2), z3 = mixin(ACCESS_COL!6);
immutable int z1 = (z2 + z3)*FIX_0_541196100;
immutable int tmp2 = z1 + z3*(-FIX_1_847759065);
immutable int tmp3 = z1 + z2*FIX_0_765366865;
immutable int tmp0 = (mixin(ACCESS_COL!0) + mixin(ACCESS_COL!4)) << CONST_BITS;
immutable int tmp1 = (mixin(ACCESS_COL!0) - mixin(ACCESS_COL!4)) << CONST_BITS;
immutable int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2;
immutable int atmp0 = mixin(ACCESS_COL!7), atmp1 = mixin(ACCESS_COL!5), atmp2 = mixin(ACCESS_COL!3), atmp3 = mixin(ACCESS_COL!1);
immutable int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3;
immutable int bz5 = (bz3 + bz4)*FIX_1_175875602;
immutable int az1 = bz1*(-FIX_0_899976223);
immutable int az2 = bz2*(-FIX_2_562915447);
immutable int az3 = bz3*(-FIX_1_961570560) + bz5;
immutable int az4 = bz4*(-FIX_0_390180644) + bz5;
immutable int btmp0 = atmp0*FIX_0_298631336 + az1 + az3;
immutable int btmp1 = atmp1*FIX_2_053119869 + az2 + az4;
immutable int btmp2 = atmp2*FIX_3_072711026 + az2 + az3;
immutable int btmp3 = atmp3*FIX_1_501321110 + az1 + az4;
pTemp[0] = DESCALE(tmp10 + btmp3, CONST_BITS-PASS1_BITS);
pTemp[7] = DESCALE(tmp10 - btmp3, CONST_BITS-PASS1_BITS);
pTemp[1] = DESCALE(tmp11 + btmp2, CONST_BITS-PASS1_BITS);
pTemp[6] = DESCALE(tmp11 - btmp2, CONST_BITS-PASS1_BITS);
pTemp[2] = DESCALE(tmp12 + btmp1, CONST_BITS-PASS1_BITS);
pTemp[5] = DESCALE(tmp12 - btmp1, CONST_BITS-PASS1_BITS);
pTemp[3] = DESCALE(tmp13 + btmp0, CONST_BITS-PASS1_BITS);
pTemp[4] = DESCALE(tmp13 - btmp0, CONST_BITS-PASS1_BITS);
}
}
}
// Compiler creates a fast path 1D IDCT for X non-zero rows
struct Col (int NONZERO_ROWS) {
pure nothrow @trusted @nogc:
static void idct(ubyte* pDst_ptr, const(int)* pTemp) {
static assert(NONZERO_ROWS > 0);
static if (NONZERO_ROWS == 1) {
int dcval = DESCALE_ZEROSHIFT(pTemp[0], PASS1_BITS+3);
immutable ubyte dcval_clamped = cast(ubyte)CLAMP(dcval);
pDst_ptr[0*8] = dcval_clamped;
pDst_ptr[1*8] = dcval_clamped;
pDst_ptr[2*8] = dcval_clamped;
pDst_ptr[3*8] = dcval_clamped;
pDst_ptr[4*8] = dcval_clamped;
pDst_ptr[5*8] = dcval_clamped;
pDst_ptr[6*8] = dcval_clamped;
pDst_ptr[7*8] = dcval_clamped;
} else {
// ACCESS_ROW() will be optimized at compile time to either an array access, or 0.
//#define ACCESS_ROW(x) (((x) < NONZERO_ROWS) ? pTemp[x * 8] : 0)
template ACCESS_ROW(int x) {
static if (x < NONZERO_ROWS) enum ACCESS_ROW = "pTemp["~(x*8).stringof~"]"; else enum ACCESS_ROW = "0";
}
immutable int z2 = mixin(ACCESS_ROW!2);
immutable int z3 = mixin(ACCESS_ROW!6);
immutable int z1 = (z2 + z3)*FIX_0_541196100;
immutable int tmp2 = z1 + z3*(-FIX_1_847759065);
immutable int tmp3 = z1 + z2*FIX_0_765366865;
immutable int tmp0 = (mixin(ACCESS_ROW!0) + mixin(ACCESS_ROW!4)) << CONST_BITS;
immutable int tmp1 = (mixin(ACCESS_ROW!0) - mixin(ACCESS_ROW!4)) << CONST_BITS;
immutable int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2;
immutable int atmp0 = mixin(ACCESS_ROW!7), atmp1 = mixin(ACCESS_ROW!5), atmp2 = mixin(ACCESS_ROW!3), atmp3 = mixin(ACCESS_ROW!1);
immutable int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3;
immutable int bz5 = (bz3 + bz4)*FIX_1_175875602;
immutable int az1 = bz1*(-FIX_0_899976223);
immutable int az2 = bz2*(-FIX_2_562915447);
immutable int az3 = bz3*(-FIX_1_961570560) + bz5;
immutable int az4 = bz4*(-FIX_0_390180644) + bz5;
immutable int btmp0 = atmp0*FIX_0_298631336 + az1 + az3;
immutable int btmp1 = atmp1*FIX_2_053119869 + az2 + az4;
immutable int btmp2 = atmp2*FIX_3_072711026 + az2 + az3;
immutable int btmp3 = atmp3*FIX_1_501321110 + az1 + az4;
int i = DESCALE_ZEROSHIFT(tmp10 + btmp3, CONST_BITS+PASS1_BITS+3);
pDst_ptr[8*0] = cast(ubyte)CLAMP(i);
i = DESCALE_ZEROSHIFT(tmp10 - btmp3, CONST_BITS+PASS1_BITS+3);
pDst_ptr[8*7] = cast(ubyte)CLAMP(i);
i = DESCALE_ZEROSHIFT(tmp11 + btmp2, CONST_BITS+PASS1_BITS+3);
pDst_ptr[8*1] = cast(ubyte)CLAMP(i);
i = DESCALE_ZEROSHIFT(tmp11 - btmp2, CONST_BITS+PASS1_BITS+3);
pDst_ptr[8*6] = cast(ubyte)CLAMP(i);
i = DESCALE_ZEROSHIFT(tmp12 + btmp1, CONST_BITS+PASS1_BITS+3);
pDst_ptr[8*2] = cast(ubyte)CLAMP(i);
i = DESCALE_ZEROSHIFT(tmp12 - btmp1, CONST_BITS+PASS1_BITS+3);
pDst_ptr[8*5] = cast(ubyte)CLAMP(i);
i = DESCALE_ZEROSHIFT(tmp13 + btmp0, CONST_BITS+PASS1_BITS+3);
pDst_ptr[8*3] = cast(ubyte)CLAMP(i);
i = DESCALE_ZEROSHIFT(tmp13 - btmp0, CONST_BITS+PASS1_BITS+3);
pDst_ptr[8*4] = cast(ubyte)CLAMP(i);
}
}
}
static immutable ubyte[512] s_idct_row_table = [
1,0,0,0,0,0,0,0, 2,0,0,0,0,0,0,0, 2,1,0,0,0,0,0,0, 2,1,1,0,0,0,0,0, 2,2,1,0,0,0,0,0, 3,2,1,0,0,0,0,0, 4,2,1,0,0,0,0,0, 4,3,1,0,0,0,0,0,
4,3,2,0,0,0,0,0, 4,3,2,1,0,0,0,0, 4,3,2,1,1,0,0,0, 4,3,2,2,1,0,0,0, 4,3,3,2,1,0,0,0, 4,4,3,2,1,0,0,0, 5,4,3,2,1,0,0,0, 6,4,3,2,1,0,0,0,
6,5,3,2,1,0,0,0, 6,5,4,2,1,0,0,0, 6,5,4,3,1,0,0,0, 6,5,4,3,2,0,0,0, 6,5,4,3,2,1,0,0, 6,5,4,3,2,1,1,0, 6,5,4,3,2,2,1,0, 6,5,4,3,3,2,1,0,
6,5,4,4,3,2,1,0, 6,5,5,4,3,2,1,0, 6,6,5,4,3,2,1,0, 7,6,5,4,3,2,1,0, 8,6,5,4,3,2,1,0, 8,7,5,4,3,2,1,0, 8,7,6,4,3,2,1,0, 8,7,6,5,3,2,1,0,
8,7,6,5,4,2,1,0, 8,7,6,5,4,3,1,0, 8,7,6,5,4,3,2,0, 8,7,6,5,4,3,2,1, 8,7,6,5,4,3,2,2, 8,7,6,5,4,3,3,2, 8,7,6,5,4,4,3,2, 8,7,6,5,5,4,3,2,
8,7,6,6,5,4,3,2, 8,7,7,6,5,4,3,2, 8,8,7,6,5,4,3,2, 8,8,8,6,5,4,3,2, 8,8,8,7,5,4,3,2, 8,8,8,7,6,4,3,2, 8,8,8,7,6,5,3,2, 8,8,8,7,6,5,4,2,
8,8,8,7,6,5,4,3, 8,8,8,7,6,5,4,4, 8,8,8,7,6,5,5,4, 8,8,8,7,6,6,5,4, 8,8,8,7,7,6,5,4, 8,8,8,8,7,6,5,4, 8,8,8,8,8,6,5,4, 8,8,8,8,8,7,5,4,
8,8,8,8,8,7,6,4, 8,8,8,8,8,7,6,5, 8,8,8,8,8,7,6,6, 8,8,8,8,8,7,7,6, 8,8,8,8,8,8,7,6, 8,8,8,8,8,8,8,6, 8,8,8,8,8,8,8,7, 8,8,8,8,8,8,8,8,
];
static immutable ubyte[64] s_idct_col_table = [ 1, 1, 2, 3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 ];
void idct() (const(jpeg_decoder.jpgd_block_t)* pSrc_ptr, ubyte* pDst_ptr, int block_max_zag) {
assert(block_max_zag >= 1);
assert(block_max_zag <= 64);
if (block_max_zag <= 1)
{
int k = ((pSrc_ptr[0] + 4) >> 3) + 128;
k = CLAMP(k);
k = k | (k<<8);
k = k | (k<<16);
for (int i = 8; i > 0; i--)
{
*cast(int*)&pDst_ptr[0] = k;
*cast(int*)&pDst_ptr[4] = k;
pDst_ptr += 8;
}
return;
}
int[64] temp;
const(jpeg_decoder.jpgd_block_t)* pSrc = pSrc_ptr;
int* pTemp = temp.ptr;
const(ubyte)* pRow_tab = &s_idct_row_table.ptr[(block_max_zag - 1) * 8];
int i;
for (i = 8; i > 0; i--, pRow_tab++)
{
switch (*pRow_tab)
{
case 0: Row!(0).idct(pTemp, pSrc); break;
case 1: Row!(1).idct(pTemp, pSrc); break;
case 2: Row!(2).idct(pTemp, pSrc); break;
case 3: Row!(3).idct(pTemp, pSrc); break;
case 4: Row!(4).idct(pTemp, pSrc); break;
case 5: Row!(5).idct(pTemp, pSrc); break;
case 6: Row!(6).idct(pTemp, pSrc); break;
case 7: Row!(7).idct(pTemp, pSrc); break;
case 8: Row!(8).idct(pTemp, pSrc); break;
default: assert(0);
}
pSrc += 8;
pTemp += 8;
}
pTemp = temp.ptr;
immutable int nonzero_rows = s_idct_col_table.ptr[block_max_zag - 1];
for (i = 8; i > 0; i--)
{
switch (nonzero_rows)
{
case 1: Col!(1).idct(pDst_ptr, pTemp); break;
case 2: Col!(2).idct(pDst_ptr, pTemp); break;
case 3: Col!(3).idct(pDst_ptr, pTemp); break;
case 4: Col!(4).idct(pDst_ptr, pTemp); break;
case 5: Col!(5).idct(pDst_ptr, pTemp); break;
case 6: Col!(6).idct(pDst_ptr, pTemp); break;
case 7: Col!(7).idct(pDst_ptr, pTemp); break;
case 8: Col!(8).idct(pDst_ptr, pTemp); break;
default: assert(0);
}
pTemp++;
pDst_ptr++;
}
}
void idct_4x4() (const(jpeg_decoder.jpgd_block_t)* pSrc_ptr, ubyte* pDst_ptr) {
int[64] temp;
int* pTemp = temp.ptr;
const(jpeg_decoder.jpgd_block_t)* pSrc = pSrc_ptr;
for (int i = 4; i > 0; i--)
{
Row!(4).idct(pTemp, pSrc);
pSrc += 8;
pTemp += 8;
}
pTemp = temp.ptr;
for (int i = 8; i > 0; i--)
{
Col!(4).idct(pDst_ptr, pTemp);
pTemp++;
pDst_ptr++;
}
}
// ////////////////////////////////////////////////////////////////////////// //
struct jpeg_decoder {
private import core.stdc.string : memcpy, memset;
private:
static auto JPGD_MIN(T) (T a, T b) pure nothrow @safe @nogc { pragma(inline, true); return (a < b ? a : b); }
static auto JPGD_MAX(T) (T a, T b) pure nothrow @safe @nogc { pragma(inline, true); return (a > b ? a : b); }
alias jpgd_quant_t = short;
alias jpgd_block_t = short;
alias pDecode_block_func = void function (ref jpeg_decoder, int, int, int);
static struct huff_tables {
bool ac_table;
uint[256] look_up;
uint[256] look_up2;
ubyte[256] code_size;
uint[512] tree;
}
static struct coeff_buf {
ubyte* pData;
int block_num_x, block_num_y;
int block_len_x, block_len_y;
int block_size;
}
static struct mem_block {
mem_block* m_pNext;
size_t m_used_count;
size_t m_size;
char[1] m_data;
}
mem_block* m_pMem_blocks;
int m_image_x_size;
int m_image_y_size;
JpegStreamReadFunc readfn;
int m_progressive_flag;
ubyte[JPGD_MAX_HUFF_TABLES] m_huff_ac;
ubyte*[JPGD_MAX_HUFF_TABLES] m_huff_num; // pointer to number of Huffman codes per bit size
ubyte*[JPGD_MAX_HUFF_TABLES] m_huff_val; // pointer to Huffman codes per bit size
jpgd_quant_t*[JPGD_MAX_QUANT_TABLES] m_quant; // pointer to quantization tables
int m_scan_type; // Gray, Yh1v1, Yh1v2, Yh2v1, Yh2v2 (CMYK111, CMYK4114 no longer supported)
int m_comps_in_frame; // # of components in frame
int[JPGD_MAX_COMPONENTS] m_comp_h_samp; // component's horizontal sampling factor
int[JPGD_MAX_COMPONENTS] m_comp_v_samp; // component's vertical sampling factor
int[JPGD_MAX_COMPONENTS] m_comp_quant; // component's quantization table selector
int[JPGD_MAX_COMPONENTS] m_comp_ident; // component's ID
int[JPGD_MAX_COMPONENTS] m_comp_h_blocks;
int[JPGD_MAX_COMPONENTS] m_comp_v_blocks;
int m_comps_in_scan; // # of components in scan
int[JPGD_MAX_COMPS_IN_SCAN] m_comp_list; // components in this scan
int[JPGD_MAX_COMPONENTS] m_comp_dc_tab; // component's DC Huffman coding table selector
int[JPGD_MAX_COMPONENTS] m_comp_ac_tab; // component's AC Huffman coding table selector
int m_spectral_start; // spectral selection start
int m_spectral_end; // spectral selection end
int m_successive_low; // successive approximation low
int m_successive_high; // successive approximation high
int m_max_mcu_x_size; // MCU's max. X size in pixels
int m_max_mcu_y_size; // MCU's max. Y size in pixels
int m_blocks_per_mcu;
int m_max_blocks_per_row;
int m_mcus_per_row, m_mcus_per_col;
int[JPGD_MAX_BLOCKS_PER_MCU] m_mcu_org;
int m_total_lines_left; // total # lines left in image
int m_mcu_lines_left; // total # lines left in this MCU
int m_real_dest_bytes_per_scan_line;
int m_dest_bytes_per_scan_line; // rounded up
int m_dest_bytes_per_pixel; // 4 (RGB) or 1 (Y)
huff_tables*[JPGD_MAX_HUFF_TABLES] m_pHuff_tabs;
coeff_buf*[JPGD_MAX_COMPONENTS] m_dc_coeffs;
coeff_buf*[JPGD_MAX_COMPONENTS] m_ac_coeffs;
int m_eob_run;
int[JPGD_MAX_COMPONENTS] m_block_y_mcu;
ubyte* m_pIn_buf_ofs;
int m_in_buf_left;
int m_tem_flag;
bool m_eof_flag;
ubyte[128] m_in_buf_pad_start;
ubyte[JPGD_IN_BUF_SIZE+128] m_in_buf;
ubyte[128] m_in_buf_pad_end;
int m_bits_left;
uint m_bit_buf;
int m_restart_interval;
int m_restarts_left;
int m_next_restart_num;
int m_max_mcus_per_row;
int m_max_blocks_per_mcu;
int m_expanded_blocks_per_mcu;
int m_expanded_blocks_per_row;
int m_expanded_blocks_per_component;
bool m_freq_domain_chroma_upsample;
int m_max_mcus_per_col;
uint[JPGD_MAX_COMPONENTS] m_last_dc_val;
jpgd_block_t* m_pMCU_coefficients;
int[JPGD_MAX_BLOCKS_PER_MCU] m_mcu_block_max_zag;
ubyte* m_pSample_buf;
int[256] m_crr;
int[256] m_cbb;
int[256] m_crg;
int[256] m_cbg;
ubyte* m_pScan_line_0;
ubyte* m_pScan_line_1;
jpgd_status m_error_code;
bool m_ready_flag;
int m_total_bytes_read;
public:
// Inspect `error_code` after constructing to determine if the stream is valid or not. You may look at the `width`, `height`, etc.
// methods after the constructor is called. You may then either destruct the object, or begin decoding the image by calling begin_decoding(), then decode() on each scanline.
this (JpegStreamReadFunc rfn) { decode_init(rfn); }
~this () { free_all_blocks(); }
@disable this (this); // no copies
// Call this method after constructing the object to begin decompression.
// If JPGD_SUCCESS is returned you may then call decode() on each scanline.
int begin_decoding () {
if (m_ready_flag) return JPGD_SUCCESS;
if (m_error_code) return JPGD_FAILED;
try {
decode_start();
m_ready_flag = true;
return JPGD_SUCCESS;
} catch (Exception e) {
//version(jpegd_test) {{ import core.stdc.stdio; stderr.fprintf("ERROR: %.*s...\n", cast(int)e.msg.length, e.msg.ptr); }}
version(jpegd_test) {{ import std.stdio; stderr.writeln(e.toString); }}
}
return JPGD_FAILED;
}
// Returns the next scan line.
// For grayscale images, pScan_line will point to a buffer containing 8-bit pixels (`bytes_per_pixel` will return 1).
// Otherwise, it will always point to a buffer containing 32-bit RGBA pixels (A will always be 255, and `bytes_per_pixel` will return 4).
// Returns JPGD_SUCCESS if a scan line has been returned.
// Returns JPGD_DONE if all scan lines have been returned.
// Returns JPGD_FAILED if an error occurred. Inspect `error_code` for a more info.
int decode (/*const void** */void** pScan_line, uint* pScan_line_len) {
if (m_error_code || !m_ready_flag) return JPGD_FAILED;
if (m_total_lines_left == 0) return JPGD_DONE;
try {
if (m_mcu_lines_left == 0) {
if (m_progressive_flag) load_next_row(); else decode_next_row();
// Find the EOI marker if that was the last row.
if (m_total_lines_left <= m_max_mcu_y_size) find_eoi();
m_mcu_lines_left = m_max_mcu_y_size;
}
if (m_freq_domain_chroma_upsample) {
expanded_convert();
*pScan_line = m_pScan_line_0;
} else {
switch (m_scan_type) {
case JPGD_YH2V2:
if ((m_mcu_lines_left & 1) == 0) {
H2V2Convert();
*pScan_line = m_pScan_line_0;
} else {
*pScan_line = m_pScan_line_1;
}
break;
case JPGD_YH2V1:
H2V1Convert();
*pScan_line = m_pScan_line_0;
break;
case JPGD_YH1V2:
if ((m_mcu_lines_left & 1) == 0) {
H1V2Convert();
*pScan_line = m_pScan_line_0;
} else {
*pScan_line = m_pScan_line_1;
}
break;
case JPGD_YH1V1:
H1V1Convert();
*pScan_line = m_pScan_line_0;
break;
case JPGD_GRAYSCALE:
gray_convert();
*pScan_line = m_pScan_line_0;
break;
default:
}
}
*pScan_line_len = m_real_dest_bytes_per_scan_line;
--m_mcu_lines_left;
--m_total_lines_left;
return JPGD_SUCCESS;
} catch (Exception) {}
return JPGD_FAILED;
}
@property const pure nothrow @trusted @nogc {
jpgd_status error_code () { pragma(inline, true); return m_error_code; }
int width () { pragma(inline, true); return m_image_x_size; }
int height () { pragma(inline, true); return m_image_y_size; }
int num_components () { pragma(inline, true); return m_comps_in_frame; }
int bytes_per_pixel () { pragma(inline, true); return m_dest_bytes_per_pixel; }
int bytes_per_scan_line () { pragma(inline, true); return m_image_x_size * bytes_per_pixel(); }
// Returns the total number of bytes actually consumed by the decoder (which should equal the actual size of the JPEG file).
int total_bytes_read () { pragma(inline, true); return m_total_bytes_read; }
}
private:
// Retrieve one character from the input stream.
uint get_char () {
// Any bytes remaining in buffer?
if (!m_in_buf_left) {
// Try to get more bytes.
prep_in_buffer();
// Still nothing to get?
if (!m_in_buf_left) {
// Pad the end of the stream with 0xFF 0xD9 (EOI marker)
int t = m_tem_flag;
m_tem_flag ^= 1;
return (t ? 0xD9 : 0xFF);
}
}
uint c = *m_pIn_buf_ofs++;
--m_in_buf_left;
return c;
}
// Same as previous method, except can indicate if the character is a pad character or not.
uint get_char (bool* pPadding_flag) {
if (!m_in_buf_left) {
prep_in_buffer();
if (!m_in_buf_left) {
*pPadding_flag = true;
int t = m_tem_flag;
m_tem_flag ^= 1;
return (t ? 0xD9 : 0xFF);
}
}
*pPadding_flag = false;
uint c = *m_pIn_buf_ofs++;
--m_in_buf_left;
return c;
}
// Inserts a previously retrieved character back into the input buffer.
void stuff_char (ubyte q) {
*(--m_pIn_buf_ofs) = q;
m_in_buf_left++;
}
// Retrieves one character from the input stream, but does not read past markers. Will continue to return 0xFF when a marker is encountered.
ubyte get_octet () {
bool padding_flag;
int c = get_char(&padding_flag);
if (c == 0xFF) {
if (padding_flag) return 0xFF;
c = get_char(&padding_flag);
if (padding_flag) { stuff_char(0xFF); return 0xFF; }
if (c == 0x00) return 0xFF;
stuff_char(cast(ubyte)(c));
stuff_char(0xFF);
return 0xFF;
}
return cast(ubyte)(c);
}
// Retrieves a variable number of bits from the input stream. Does not recognize markers.
uint get_bits (int num_bits) {
if (!num_bits) return 0;
uint i = m_bit_buf >> (32 - num_bits);
if ((m_bits_left -= num_bits) <= 0) {
m_bit_buf <<= (num_bits += m_bits_left);
uint c1 = get_char();
uint c2 = get_char();
m_bit_buf = (m_bit_buf & 0xFFFF0000) | (c1 << 8) | c2;
m_bit_buf <<= -m_bits_left;
m_bits_left += 16;
assert(m_bits_left >= 0);
} else {
m_bit_buf <<= num_bits;
}
return i;
}
// Retrieves a variable number of bits from the input stream. Markers will not be read into the input bit buffer. Instead, an infinite number of all 1's will be returned when a marker is encountered.
uint get_bits_no_markers (int num_bits) {
if (!num_bits) return 0;
uint i = m_bit_buf >> (32 - num_bits);
if ((m_bits_left -= num_bits) <= 0) {
m_bit_buf <<= (num_bits += m_bits_left);
if (m_in_buf_left < 2 || m_pIn_buf_ofs[0] == 0xFF || m_pIn_buf_ofs[1] == 0xFF) {
uint c1 = get_octet();
uint c2 = get_octet();
m_bit_buf |= (c1 << 8) | c2;
} else {
m_bit_buf |= (cast(uint)m_pIn_buf_ofs[0] << 8) | m_pIn_buf_ofs[1];
m_in_buf_left -= 2;
m_pIn_buf_ofs += 2;
}
m_bit_buf <<= -m_bits_left;
m_bits_left += 16;
assert(m_bits_left >= 0);
} else {
m_bit_buf <<= num_bits;
}
return i;
}
// Decodes a Huffman encoded symbol.
int huff_decode (huff_tables *pH) {
int symbol;
// Check first 8-bits: do we have a complete symbol?
if ((symbol = pH.look_up.ptr[m_bit_buf >> 24]) < 0) {
// Decode more bits, use a tree traversal to find symbol.
int ofs = 23;
do {
symbol = pH.tree.ptr[-cast(int)(symbol + ((m_bit_buf >> ofs) & 1))];
--ofs;
} while (symbol < 0);
get_bits_no_markers(8 + (23 - ofs));
} else {
get_bits_no_markers(pH.code_size.ptr[symbol]);
}
return symbol;
}
// Decodes a Huffman encoded symbol.
int huff_decode (huff_tables *pH, ref int extra_bits) {
int symbol;
// Check first 8-bits: do we have a complete symbol?
if ((symbol = pH.look_up2.ptr[m_bit_buf >> 24]) < 0) {
// Use a tree traversal to find symbol.
int ofs = 23;
do {
symbol = pH.tree.ptr[-cast(int)(symbol + ((m_bit_buf >> ofs) & 1))];
--ofs;
} while (symbol < 0);
get_bits_no_markers(8 + (23 - ofs));
extra_bits = get_bits_no_markers(symbol & 0xF);
} else {
assert(((symbol >> 8) & 31) == pH.code_size.ptr[symbol & 255] + ((symbol & 0x8000) ? (symbol & 15) : 0));
if (symbol & 0x8000) {
get_bits_no_markers((symbol >> 8) & 31);
extra_bits = symbol >> 16;
} else {
int code_size = (symbol >> 8) & 31;
int num_extra_bits = symbol & 0xF;
int bits = code_size + num_extra_bits;
if (bits <= (m_bits_left + 16)) {
extra_bits = get_bits_no_markers(bits) & ((1 << num_extra_bits) - 1);
} else {
get_bits_no_markers(code_size);
extra_bits = get_bits_no_markers(num_extra_bits);
}
}
symbol &= 0xFF;
}
return symbol;
}
// Tables and macro used to fully decode the DPCM differences.
static immutable int[16] s_extend_test = [ 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 ];
static immutable int[16] s_extend_offset = [ 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 ];
static immutable int[18] s_extend_mask = [ 0, (1<<0), (1<<1), (1<<2), (1<<3), (1<<4), (1<<5), (1<<6), (1<<7), (1<<8), (1<<9), (1<<10), (1<<11), (1<<12), (1<<13), (1<<14), (1<<15), (1<<16) ];
// The logical AND's in this macro are to shut up static code analysis (aren't really necessary - couldn't find another way to do this)
//#define JPGD_HUFF_EXTEND(x, s) (((x) < s_extend_test[s & 15]) ? ((x) + s_extend_offset[s & 15]) : (x))
static JPGD_HUFF_EXTEND (int x, int s) nothrow @trusted @nogc { pragma(inline, true); return (((x) < s_extend_test.ptr[s & 15]) ? ((x) + s_extend_offset.ptr[s & 15]) : (x)); }
// Clamps a value between 0-255.
//static ubyte clamp (int i) { if (cast(uint)(i) > 255) i = (((~i) >> 31) & 0xFF); return cast(ubyte)(i); }
alias clamp = CLAMP;
static struct DCT_Upsample {
static:
static struct Matrix44 {
pure nothrow @trusted @nogc:
alias Element_Type = int;
enum { NUM_ROWS = 4, NUM_COLS = 4 }
Element_Type[NUM_COLS][NUM_ROWS] v;
this() (const scope auto ref Matrix44 m) {
foreach (immutable r; 0..NUM_ROWS) v[r][] = m.v[r][];
}
//@property int rows () const { pragma(inline, true); return NUM_ROWS; }
//@property int cols () const { pragma(inline, true); return NUM_COLS; }
ref inout(Element_Type) at (int r, int c) inout { pragma(inline, true); return v.ptr[r].ptr[c]; }
ref Matrix44 opOpAssign(string op:"+") (const scope auto ref Matrix44 a) {
foreach (int r; 0..NUM_ROWS) {
at(r, 0) += a.at(r, 0);
at(r, 1) += a.at(r, 1);
at(r, 2) += a.at(r, 2);
at(r, 3) += a.at(r, 3);
}
return this;
}
ref Matrix44 opOpAssign(string op:"-") (const scope auto ref Matrix44 a) {
foreach (int r; 0..NUM_ROWS) {
at(r, 0) -= a.at(r, 0);
at(r, 1) -= a.at(r, 1);
at(r, 2) -= a.at(r, 2);
at(r, 3) -= a.at(r, 3);
}
return this;
}
Matrix44 opBinary(string op:"+") (const scope auto ref Matrix44 b) const {
alias a = this;
Matrix44 ret;
foreach (int r; 0..NUM_ROWS) {
ret.at(r, 0) = a.at(r, 0) + b.at(r, 0);
ret.at(r, 1) = a.at(r, 1) + b.at(r, 1);
ret.at(r, 2) = a.at(r, 2) + b.at(r, 2);
ret.at(r, 3) = a.at(r, 3) + b.at(r, 3);
}
return ret;
}
Matrix44 opBinary(string op:"-") (const scope auto ref Matrix44 b) const {
alias a = this;
Matrix44 ret;
foreach (int r; 0..NUM_ROWS) {
ret.at(r, 0) = a.at(r, 0) - b.at(r, 0);
ret.at(r, 1) = a.at(r, 1) - b.at(r, 1);
ret.at(r, 2) = a.at(r, 2) - b.at(r, 2);
ret.at(r, 3) = a.at(r, 3) - b.at(r, 3);
}
return ret;
}
static void add_and_store() (jpgd_block_t* pDst, const scope auto ref Matrix44 a, const scope auto ref Matrix44 b) {
foreach (int r; 0..4) {
pDst[0*8 + r] = cast(jpgd_block_t)(a.at(r, 0) + b.at(r, 0));
pDst[1*8 + r] = cast(jpgd_block_t)(a.at(r, 1) + b.at(r, 1));
pDst[2*8 + r] = cast(jpgd_block_t)(a.at(r, 2) + b.at(r, 2));
pDst[3*8 + r] = cast(jpgd_block_t)(a.at(r, 3) + b.at(r, 3));
}
}
static void sub_and_store() (jpgd_block_t* pDst, const scope auto ref Matrix44 a, const scope auto ref Matrix44 b) {
foreach (int r; 0..4) {
pDst[0*8 + r] = cast(jpgd_block_t)(a.at(r, 0) - b.at(r, 0));
pDst[1*8 + r] = cast(jpgd_block_t)(a.at(r, 1) - b.at(r, 1));
pDst[2*8 + r] = cast(jpgd_block_t)(a.at(r, 2) - b.at(r, 2));
pDst[3*8 + r] = cast(jpgd_block_t)(a.at(r, 3) - b.at(r, 3));
}
}
}
enum FRACT_BITS = 10;
enum SCALE = 1 << FRACT_BITS;
alias Temp_Type = int;
//TODO: convert defines to mixins
//#define D(i) (((i) + (SCALE >> 1)) >> FRACT_BITS)
//#define F(i) ((int)((i) * SCALE + .5f))
// Any decent C++ compiler will optimize this at compile time to a 0, or an array access.
//#define AT(c, r) ((((c)>=NUM_COLS)||((r)>=NUM_ROWS)) ? 0 : pSrc[(c)+(r)*8])
static int D(T) (T i) { pragma(inline, true); return (((i) + (SCALE >> 1)) >> FRACT_BITS); }
enum F(float i) = (cast(int)((i) * SCALE + 0.5f));
// NUM_ROWS/NUM_COLS = # of non-zero rows/cols in input matrix
static struct P_Q(int NUM_ROWS, int NUM_COLS) {
static void calc (ref Matrix44 P, ref Matrix44 Q, const(jpgd_block_t)* pSrc) {
//auto AT (int c, int r) nothrow @trusted @nogc { return (c >= NUM_COLS || r >= NUM_ROWS ? 0 : pSrc[c+r*8]); }
template AT(int c, int r) {
static if (c >= NUM_COLS || r >= NUM_ROWS) enum AT = "0"; else enum AT = "pSrc["~c.stringof~"+"~r.stringof~"*8]";
}
// 4x8 = 4x8 times 8x8, matrix 0 is constant
immutable Temp_Type X000 = mixin(AT!(0, 0));
immutable Temp_Type X001 = mixin(AT!(0, 1));
immutable Temp_Type X002 = mixin(AT!(0, 2));
immutable Temp_Type X003 = mixin(AT!(0, 3));
immutable Temp_Type X004 = mixin(AT!(0, 4));
immutable Temp_Type X005 = mixin(AT!(0, 5));
immutable Temp_Type X006 = mixin(AT!(0, 6));
immutable Temp_Type X007 = mixin(AT!(0, 7));
immutable Temp_Type X010 = D(F!(0.415735f) * mixin(AT!(1, 0)) + F!(0.791065f) * mixin(AT!(3, 0)) + F!(-0.352443f) * mixin(AT!(5, 0)) + F!(0.277785f) * mixin(AT!(7, 0)));
immutable Temp_Type X011 = D(F!(0.415735f) * mixin(AT!(1, 1)) + F!(0.791065f) * mixin(AT!(3, 1)) + F!(-0.352443f) * mixin(AT!(5, 1)) + F!(0.277785f) * mixin(AT!(7, 1)));
immutable Temp_Type X012 = D(F!(0.415735f) * mixin(AT!(1, 2)) + F!(0.791065f) * mixin(AT!(3, 2)) + F!(-0.352443f) * mixin(AT!(5, 2)) + F!(0.277785f) * mixin(AT!(7, 2)));
immutable Temp_Type X013 = D(F!(0.415735f) * mixin(AT!(1, 3)) + F!(0.791065f) * mixin(AT!(3, 3)) + F!(-0.352443f) * mixin(AT!(5, 3)) + F!(0.277785f) * mixin(AT!(7, 3)));
immutable Temp_Type X014 = D(F!(0.415735f) * mixin(AT!(1, 4)) + F!(0.791065f) * mixin(AT!(3, 4)) + F!(-0.352443f) * mixin(AT!(5, 4)) + F!(0.277785f) * mixin(AT!(7, 4)));
immutable Temp_Type X015 = D(F!(0.415735f) * mixin(AT!(1, 5)) + F!(0.791065f) * mixin(AT!(3, 5)) + F!(-0.352443f) * mixin(AT!(5, 5)) + F!(0.277785f) * mixin(AT!(7, 5)));
immutable Temp_Type X016 = D(F!(0.415735f) * mixin(AT!(1, 6)) + F!(0.791065f) * mixin(AT!(3, 6)) + F!(-0.352443f) * mixin(AT!(5, 6)) + F!(0.277785f) * mixin(AT!(7, 6)));
immutable Temp_Type X017 = D(F!(0.415735f) * mixin(AT!(1, 7)) + F!(0.791065f) * mixin(AT!(3, 7)) + F!(-0.352443f) * mixin(AT!(5, 7)) + F!(0.277785f) * mixin(AT!(7, 7)));
immutable Temp_Type X020 = mixin(AT!(4, 0));
immutable Temp_Type X021 = mixin(AT!(4, 1));
immutable Temp_Type X022 = mixin(AT!(4, 2));
immutable Temp_Type X023 = mixin(AT!(4, 3));
immutable Temp_Type X024 = mixin(AT!(4, 4));
immutable Temp_Type X025 = mixin(AT!(4, 5));
immutable Temp_Type X026 = mixin(AT!(4, 6));
immutable Temp_Type X027 = mixin(AT!(4, 7));
immutable Temp_Type X030 = D(F!(0.022887f) * mixin(AT!(1, 0)) + F!(-0.097545f) * mixin(AT!(3, 0)) + F!(0.490393f) * mixin(AT!(5, 0)) + F!(0.865723f) * mixin(AT!(7, 0)));
immutable Temp_Type X031 = D(F!(0.022887f) * mixin(AT!(1, 1)) + F!(-0.097545f) * mixin(AT!(3, 1)) + F!(0.490393f) * mixin(AT!(5, 1)) + F!(0.865723f) * mixin(AT!(7, 1)));
immutable Temp_Type X032 = D(F!(0.022887f) * mixin(AT!(1, 2)) + F!(-0.097545f) * mixin(AT!(3, 2)) + F!(0.490393f) * mixin(AT!(5, 2)) + F!(0.865723f) * mixin(AT!(7, 2)));
immutable Temp_Type X033 = D(F!(0.022887f) * mixin(AT!(1, 3)) + F!(-0.097545f) * mixin(AT!(3, 3)) + F!(0.490393f) * mixin(AT!(5, 3)) + F!(0.865723f) * mixin(AT!(7, 3)));
immutable Temp_Type X034 = D(F!(0.022887f) * mixin(AT!(1, 4)) + F!(-0.097545f) * mixin(AT!(3, 4)) + F!(0.490393f) * mixin(AT!(5, 4)) + F!(0.865723f) * mixin(AT!(7, 4)));
immutable Temp_Type X035 = D(F!(0.022887f) * mixin(AT!(1, 5)) + F!(-0.097545f) * mixin(AT!(3, 5)) + F!(0.490393f) * mixin(AT!(5, 5)) + F!(0.865723f) * mixin(AT!(7, 5)));
immutable Temp_Type X036 = D(F!(0.022887f) * mixin(AT!(1, 6)) + F!(-0.097545f) * mixin(AT!(3, 6)) + F!(0.490393f) * mixin(AT!(5, 6)) + F!(0.865723f) * mixin(AT!(7, 6)));
immutable Temp_Type X037 = D(F!(0.022887f) * mixin(AT!(1, 7)) + F!(-0.097545f) * mixin(AT!(3, 7)) + F!(0.490393f) * mixin(AT!(5, 7)) + F!(0.865723f) * mixin(AT!(7, 7)));
// 4x4 = 4x8 times 8x4, matrix 1 is constant
P.at(0, 0) = X000;
P.at(0, 1) = D(X001 * F!(0.415735f) + X003 * F!(0.791065f) + X005 * F!(-0.352443f) + X007 * F!(0.277785f));
P.at(0, 2) = X004;
P.at(0, 3) = D(X001 * F!(0.022887f) + X003 * F!(-0.097545f) + X005 * F!(0.490393f) + X007 * F!(0.865723f));
P.at(1, 0) = X010;
P.at(1, 1) = D(X011 * F!(0.415735f) + X013 * F!(0.791065f) + X015 * F!(-0.352443f) + X017 * F!(0.277785f));
P.at(1, 2) = X014;
P.at(1, 3) = D(X011 * F!(0.022887f) + X013 * F!(-0.097545f) + X015 * F!(0.490393f) + X017 * F!(0.865723f));
P.at(2, 0) = X020;
P.at(2, 1) = D(X021 * F!(0.415735f) + X023 * F!(0.791065f) + X025 * F!(-0.352443f) + X027 * F!(0.277785f));
P.at(2, 2) = X024;
P.at(2, 3) = D(X021 * F!(0.022887f) + X023 * F!(-0.097545f) + X025 * F!(0.490393f) + X027 * F!(0.865723f));
P.at(3, 0) = X030;
P.at(3, 1) = D(X031 * F!(0.415735f) + X033 * F!(0.791065f) + X035 * F!(-0.352443f) + X037 * F!(0.277785f));
P.at(3, 2) = X034;
P.at(3, 3) = D(X031 * F!(0.022887f) + X033 * F!(-0.097545f) + X035 * F!(0.490393f) + X037 * F!(0.865723f));
// 40 muls 24 adds
// 4x4 = 4x8 times 8x4, matrix 1 is constant
Q.at(0, 0) = D(X001 * F!(0.906127f) + X003 * F!(-0.318190f) + X005 * F!(0.212608f) + X007 * F!(-0.180240f));
Q.at(0, 1) = X002;
Q.at(0, 2) = D(X001 * F!(-0.074658f) + X003 * F!(0.513280f) + X005 * F!(0.768178f) + X007 * F!(-0.375330f));
Q.at(0, 3) = X006;
Q.at(1, 0) = D(X011 * F!(0.906127f) + X013 * F!(-0.318190f) + X015 * F!(0.212608f) + X017 * F!(-0.180240f));
Q.at(1, 1) = X012;
Q.at(1, 2) = D(X011 * F!(-0.074658f) + X013 * F!(0.513280f) + X015 * F!(0.768178f) + X017 * F!(-0.375330f));
Q.at(1, 3) = X016;
Q.at(2, 0) = D(X021 * F!(0.906127f) + X023 * F!(-0.318190f) + X025 * F!(0.212608f) + X027 * F!(-0.180240f));
Q.at(2, 1) = X022;
Q.at(2, 2) = D(X021 * F!(-0.074658f) + X023 * F!(0.513280f) + X025 * F!(0.768178f) + X027 * F!(-0.375330f));
Q.at(2, 3) = X026;
Q.at(3, 0) = D(X031 * F!(0.906127f) + X033 * F!(-0.318190f) + X035 * F!(0.212608f) + X037 * F!(-0.180240f));
Q.at(3, 1) = X032;
Q.at(3, 2) = D(X031 * F!(-0.074658f) + X033 * F!(0.513280f) + X035 * F!(0.768178f) + X037 * F!(-0.375330f));
Q.at(3, 3) = X036;
// 40 muls 24 adds
}
}
static struct R_S(int NUM_ROWS, int NUM_COLS) {
static void calc(ref Matrix44 R, ref Matrix44 S, const(jpgd_block_t)* pSrc) {
//auto AT (int c, int r) nothrow @trusted @nogc { return (c >= NUM_COLS || r >= NUM_ROWS ? 0 : pSrc[c+r*8]); }
template AT(int c, int r) {
static if (c >= NUM_COLS || r >= NUM_ROWS) enum AT = "0"; else enum AT = "pSrc["~c.stringof~"+"~r.stringof~"*8]";
}
// 4x8 = 4x8 times 8x8, matrix 0 is constant
immutable Temp_Type X100 = D(F!(0.906127f) * mixin(AT!(1, 0)) + F!(-0.318190f) * mixin(AT!(3, 0)) + F!(0.212608f) * mixin(AT!(5, 0)) + F!(-0.180240f) * mixin(AT!(7, 0)));
immutable Temp_Type X101 = D(F!(0.906127f) * mixin(AT!(1, 1)) + F!(-0.318190f) * mixin(AT!(3, 1)) + F!(0.212608f) * mixin(AT!(5, 1)) + F!(-0.180240f) * mixin(AT!(7, 1)));
immutable Temp_Type X102 = D(F!(0.906127f) * mixin(AT!(1, 2)) + F!(-0.318190f) * mixin(AT!(3, 2)) + F!(0.212608f) * mixin(AT!(5, 2)) + F!(-0.180240f) * mixin(AT!(7, 2)));
immutable Temp_Type X103 = D(F!(0.906127f) * mixin(AT!(1, 3)) + F!(-0.318190f) * mixin(AT!(3, 3)) + F!(0.212608f) * mixin(AT!(5, 3)) + F!(-0.180240f) * mixin(AT!(7, 3)));
immutable Temp_Type X104 = D(F!(0.906127f) * mixin(AT!(1, 4)) + F!(-0.318190f) * mixin(AT!(3, 4)) + F!(0.212608f) * mixin(AT!(5, 4)) + F!(-0.180240f) * mixin(AT!(7, 4)));
immutable Temp_Type X105 = D(F!(0.906127f) * mixin(AT!(1, 5)) + F!(-0.318190f) * mixin(AT!(3, 5)) + F!(0.212608f) * mixin(AT!(5, 5)) + F!(-0.180240f) * mixin(AT!(7, 5)));
immutable Temp_Type X106 = D(F!(0.906127f) * mixin(AT!(1, 6)) + F!(-0.318190f) * mixin(AT!(3, 6)) + F!(0.212608f) * mixin(AT!(5, 6)) + F!(-0.180240f) * mixin(AT!(7, 6)));
immutable Temp_Type X107 = D(F!(0.906127f) * mixin(AT!(1, 7)) + F!(-0.318190f) * mixin(AT!(3, 7)) + F!(0.212608f) * mixin(AT!(5, 7)) + F!(-0.180240f) * mixin(AT!(7, 7)));
immutable Temp_Type X110 = mixin(AT!(2, 0));
immutable Temp_Type X111 = mixin(AT!(2, 1));
immutable Temp_Type X112 = mixin(AT!(2, 2));
immutable Temp_Type X113 = mixin(AT!(2, 3));
immutable Temp_Type X114 = mixin(AT!(2, 4));
immutable Temp_Type X115 = mixin(AT!(2, 5));
immutable Temp_Type X116 = mixin(AT!(2, 6));
immutable Temp_Type X117 = mixin(AT!(2, 7));
immutable Temp_Type X120 = D(F!(-0.074658f) * mixin(AT!(1, 0)) + F!(0.513280f) * mixin(AT!(3, 0)) + F!(0.768178f) * mixin(AT!(5, 0)) + F!(-0.375330f) * mixin(AT!(7, 0)));
immutable Temp_Type X121 = D(F!(-0.074658f) * mixin(AT!(1, 1)) + F!(0.513280f) * mixin(AT!(3, 1)) + F!(0.768178f) * mixin(AT!(5, 1)) + F!(-0.375330f) * mixin(AT!(7, 1)));
immutable Temp_Type X122 = D(F!(-0.074658f) * mixin(AT!(1, 2)) + F!(0.513280f) * mixin(AT!(3, 2)) + F!(0.768178f) * mixin(AT!(5, 2)) + F!(-0.375330f) * mixin(AT!(7, 2)));
immutable Temp_Type X123 = D(F!(-0.074658f) * mixin(AT!(1, 3)) + F!(0.513280f) * mixin(AT!(3, 3)) + F!(0.768178f) * mixin(AT!(5, 3)) + F!(-0.375330f) * mixin(AT!(7, 3)));
immutable Temp_Type X124 = D(F!(-0.074658f) * mixin(AT!(1, 4)) + F!(0.513280f) * mixin(AT!(3, 4)) + F!(0.768178f) * mixin(AT!(5, 4)) + F!(-0.375330f) * mixin(AT!(7, 4)));
immutable Temp_Type X125 = D(F!(-0.074658f) * mixin(AT!(1, 5)) + F!(0.513280f) * mixin(AT!(3, 5)) + F!(0.768178f) * mixin(AT!(5, 5)) + F!(-0.375330f) * mixin(AT!(7, 5)));
immutable Temp_Type X126 = D(F!(-0.074658f) * mixin(AT!(1, 6)) + F!(0.513280f) * mixin(AT!(3, 6)) + F!(0.768178f) * mixin(AT!(5, 6)) + F!(-0.375330f) * mixin(AT!(7, 6)));
immutable Temp_Type X127 = D(F!(-0.074658f) * mixin(AT!(1, 7)) + F!(0.513280f) * mixin(AT!(3, 7)) + F!(0.768178f) * mixin(AT!(5, 7)) + F!(-0.375330f) * mixin(AT!(7, 7)));
immutable Temp_Type X130 = mixin(AT!(6, 0));
immutable Temp_Type X131 = mixin(AT!(6, 1));
immutable Temp_Type X132 = mixin(AT!(6, 2));
immutable Temp_Type X133 = mixin(AT!(6, 3));
immutable Temp_Type X134 = mixin(AT!(6, 4));
immutable Temp_Type X135 = mixin(AT!(6, 5));
immutable Temp_Type X136 = mixin(AT!(6, 6));
immutable Temp_Type X137 = mixin(AT!(6, 7));
// 80 muls 48 adds
// 4x4 = 4x8 times 8x4, matrix 1 is constant
R.at(0, 0) = X100;
R.at(0, 1) = D(X101 * F!(0.415735f) + X103 * F!(0.791065f) + X105 * F!(-0.352443f) + X107 * F!(0.277785f));
R.at(0, 2) = X104;
R.at(0, 3) = D(X101 * F!(0.022887f) + X103 * F!(-0.097545f) + X105 * F!(0.490393f) + X107 * F!(0.865723f));
R.at(1, 0) = X110;
R.at(1, 1) = D(X111 * F!(0.415735f) + X113 * F!(0.791065f) + X115 * F!(-0.352443f) + X117 * F!(0.277785f));
R.at(1, 2) = X114;
R.at(1, 3) = D(X111 * F!(0.022887f) + X113 * F!(-0.097545f) + X115 * F!(0.490393f) + X117 * F!(0.865723f));
R.at(2, 0) = X120;
R.at(2, 1) = D(X121 * F!(0.415735f) + X123 * F!(0.791065f) + X125 * F!(-0.352443f) + X127 * F!(0.277785f));
R.at(2, 2) = X124;
R.at(2, 3) = D(X121 * F!(0.022887f) + X123 * F!(-0.097545f) + X125 * F!(0.490393f) + X127 * F!(0.865723f));
R.at(3, 0) = X130;
R.at(3, 1) = D(X131 * F!(0.415735f) + X133 * F!(0.791065f) + X135 * F!(-0.352443f) + X137 * F!(0.277785f));
R.at(3, 2) = X134;
R.at(3, 3) = D(X131 * F!(0.022887f) + X133 * F!(-0.097545f) + X135 * F!(0.490393f) + X137 * F!(0.865723f));
// 40 muls 24 adds
// 4x4 = 4x8 times 8x4, matrix 1 is constant
S.at(0, 0) = D(X101 * F!(0.906127f) + X103 * F!(-0.318190f) + X105 * F!(0.212608f) + X107 * F!(-0.180240f));
S.at(0, 1) = X102;
S.at(0, 2) = D(X101 * F!(-0.074658f) + X103 * F!(0.513280f) + X105 * F!(0.768178f) + X107 * F!(-0.375330f));
S.at(0, 3) = X106;
S.at(1, 0) = D(X111 * F!(0.906127f) + X113 * F!(-0.318190f) + X115 * F!(0.212608f) + X117 * F!(-0.180240f));
S.at(1, 1) = X112;
S.at(1, 2) = D(X111 * F!(-0.074658f) + X113 * F!(0.513280f) + X115 * F!(0.768178f) + X117 * F!(-0.375330f));
S.at(1, 3) = X116;
S.at(2, 0) = D(X121 * F!(0.906127f) + X123 * F!(-0.318190f) + X125 * F!(0.212608f) + X127 * F!(-0.180240f));
S.at(2, 1) = X122;
S.at(2, 2) = D(X121 * F!(-0.074658f) + X123 * F!(0.513280f) + X125 * F!(0.768178f) + X127 * F!(-0.375330f));
S.at(2, 3) = X126;
S.at(3, 0) = D(X131 * F!(0.906127f) + X133 * F!(-0.318190f) + X135 * F!(0.212608f) + X137 * F!(-0.180240f));
S.at(3, 1) = X132;
S.at(3, 2) = D(X131 * F!(-0.074658f) + X133 * F!(0.513280f) + X135 * F!(0.768178f) + X137 * F!(-0.375330f));
S.at(3, 3) = X136;
// 40 muls 24 adds
}
}
} // end namespace DCT_Upsample
// Unconditionally frees all allocated m_blocks.
void free_all_blocks () {
//m_pStream = null;
readfn = null;