The OpenD Programming Language

1 /// stb_image_write.h translations
2 /// Just the PNG encoder.
3 module gamut.codecs.stb_image_write;
4 
5 /* stb_image_write - v1.16 - public domain - http://nothings.org/stb
6    writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015
7                                      no warranty implied; use at your own risk
8 
9    Before #including,
10 
11        #define STB_IMAGE_WRITE_IMPLEMENTATION
12 
13    in the file that you want to have the implementation.
14 
15    Will probably not work correctly with strict-aliasing optimizations.
16 
17 ABOUT:
18 
19    This header file is a library for writing images to C stdio or a callback.
20 
21    The PNG output is not optimal; it is 20-50% larger than the file
22    written by a decent optimizing implementation; though providing a custom
23    zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that.
24    This library is designed for source code compactness and simplicity,
25    not optimal image file size or run-time performance.
26 
27 
28 USAGE:
29 
30    There are one function:
31 
32      int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
33 
34    You can configure it with these global variables:
35       int stbi_write_png_compression_level;    // defaults to 8; set to higher for more compression
36       int stbi_write_force_png_filter;         // defaults to -1; set to 0..5 to force a filter mode
37 
38    Each function returns 0 on failure and non-0 on success.
39 
40    The functions create an image file defined by the parameters. The image
41    is a rectangle of pixels stored from left-to-right, top-to-bottom.
42    Each pixel contains 'comp' channels of data stored interleaved with 8-bits
43    per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is
44    monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall.
45    The *data pointer points to the first byte of the top-left-most pixel.
46    For PNG, "stride_in_bytes" is the distance in bytes from the first byte of
47    a row of pixels to the first byte of the next row of pixels.
48 
49    PNG creates output files with the same number of components as the input.
50    The BMP format expands Y to RGB in the file format and does not
51    output alpha.
52 
53    PNG supports writing rectangles of data even when the bytes storing rows of
54    data are not consecutive in memory (e.g. sub-rectangles of a larger image),
55    by supplying the stride between the beginning of adjacent rows. The other
56    formats do not. (Thus you cannot write a native-format BMP through the BMP
57    writer, both because it is in BGR order and because it may have padding
58    at the end of the line.)
59 
60    PNG allows you to set the deflate compression level by setting the global
61    variable 'stbi_write_png_compression_level' (it defaults to 8).
62 
63 CREDITS:
64 
65 
66    Sean Barrett           -    PNG/BMP/TGA
67    Baldur Karlsson        -    HDR
68    Jean-Sebastien Guay    -    TGA monochrome
69    Tim Kelsey             -    misc enhancements
70    Alan Hickman           -    TGA RLE
71    Emmanuel Julien        -    initial file IO callback implementation
72    Jon Olick              -    original jo_jpeg.cpp code
73    Daniel Gibson          -    integrate JPEG, allow external zlib
74    Aarni Koskela          -    allow choosing PNG filter
75 
76    bugfixes:
77       github:Chribba
78       Guillaume Chereau
79       github:jry2
80       github:romigrou
81       Sergio Gonzalez
82       Jonas Karlsson
83       Filip Wasil
84       Thatcher Ulrich
85       github:poppolopoppo
86       Patrick Boettcher
87       github:xeekworx
88       Cap Petschulat
89       Simon Rodriguez
90       Ivan Tikhonov
91       github:ignotion
92       Adam Schackart
93       Andrew Kensler
94 
95 LICENSE
96 
97   See end of file for license information.
98 
99 */
100 
101 version(encodePNG)
102     version = compileSTBImageWrite;
103 version(encodeJPEG)
104     version = compileSTBImageWrite;
105 
106 version(compileSTBImageWrite):
107 
108 import std.math: abs;
109 import core.stdc.stdlib: malloc, realloc, free;
110 import core.stdc.string: memcpy, memmove;
111 import gamut.codecs.miniz;
112 
113 nothrow @nogc:
114 
115 private:
116 
117 alias STBIW_MALLOC = malloc;
118 alias STBIW_REALLOC = realloc;
119 alias STBIW_FREE = free;
120 alias STBIW_MEMMOVE = memmove;
121 
122 void* STBIW_REALLOC_SIZED(void *ptr, size_t oldsz, size_t newsz)
123 {
124     return realloc(ptr, newsz);
125 }
126 
127 ubyte STBIW_UCHAR(int x)
128 {
129     return cast(ubyte)(x);
130 }
131 
132 enum int stbi_write_png_compression_level = 5;
133 enum int stbi_write_force_png_filter = -1;
134 
135 // Useful?
136 enum int stbi__flip_vertically_on_write = 0;
137 
138 
139 alias stbiw_uint32 = uint;
140 
141 //////////////////////////////////////////////////////////////////////////////
142 //
143 // PNG writer
144 //
145 
146 
147 // write context
148 
149 alias stbi_write_func = void function(void *context, const(void)* data, int size);
150 
151 struct stbi__write_context
152 {
153     stbi_write_func func = null;
154     void* context = null;
155     ubyte[64] buffer;
156     int buf_used = 0;
157 }
158 
159 // initialize a callback-based context
160 void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func c, void *context)
161 {
162     s.func    = c;
163     s.context = context;
164 }
165 
166 static void stbiw__putc(stbi__write_context *s, ubyte c)
167 {
168     s.func(s.context, &c, 1);
169 }
170 
171 /// Free result with free
172 ubyte * stbi_zlib_compress(ubyte *data, int data_len, int *out_len, int quality)
173 {
174     size_t outLenSize_t;
175     assert(quality >= 1 && quality <= 9);
176     uint comp_flags = TDEFL_COMPUTE_ADLER32 
177         | tdefl_create_comp_flags_from_zip_params(quality, MZ_DEFAULT_WINDOW_BITS, MZ_DEFAULT_STRATEGY);
178 
179     ubyte* res = cast(ubyte*) tdefl_compress_mem_to_heap(data, data_len, &outLenSize_t, comp_flags);
180 
181     if (res)
182     {
183         assert(outLenSize_t <= int.max); // else overflow, fix this library
184         *out_len = cast(int) outLenSize_t;
185     }
186     return res;
187 }
188 
189 static uint stbiw__crc32(ubyte *buffer, int len)
190 {
191     static immutable uint[256] crc_table =
192     [
193         0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
194         0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
195         0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
196         0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
197         0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
198         0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
199         0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
200         0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
201         0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
202         0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
203         0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
204         0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
205         0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
206         0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
207         0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
208         0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
209         0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
210         0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
211         0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
212         0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
213         0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
214         0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
215         0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
216         0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
217         0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
218         0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
219         0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
220         0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
221         0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
222         0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
223         0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
224         0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
225     ];
226 
227     uint crc = ~0u;
228     int i;
229     for (i=0; i < len; ++i)
230         crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
231     return ~crc;
232 }
233 
234 void stbiw__wpng4(ref ubyte* o, ubyte a, ubyte b, ubyte c, ubyte d)
235 {
236     o[0] = a;
237     o[1] = b;
238     o[2] = c;
239     o[3] = d;
240     o += 4;
241 }
242 
243 void stbiw__wp32(ref ubyte* data, uint v)
244 {
245     stbiw__wpng4(data, v >> 24, (v >> 16) & 0xff, (v >> 8) & 0xff, v & 0xff);
246 }
247 
248 void stbiw__wptag(ref ubyte* data, char[4] s)
249 {
250     stbiw__wpng4(data, s[0], s[1], s[2], s[3]);
251 }
252 
253 static void stbiw__wpcrc(ubyte **data, int len)
254 {
255    uint crc = stbiw__crc32(*data - len - 4, len+4);
256    stbiw__wp32(*data, crc);
257 }
258 
259 ubyte stbiw__paeth(int a, int b, int c)
260 {
261    int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c);
262    if (pa <= pb && pa <= pc) return STBIW_UCHAR(a);
263    if (pb <= pc) return STBIW_UCHAR(b);
264    return STBIW_UCHAR(c);
265 }
266 
267 // @OPTIMIZE: provide an option that always forces left-predict or paeth predict
268 static void stbiw__encode_png_line(ubyte *pixels, 
269                                    int stride_bytes, 
270                                    int width, 
271                                    int height, 
272                                    int y, 
273                                    int n,
274                                    bool is16bit,
275                                    int filter_type, 
276                                    byte* line_buffer)
277 {
278    static immutable int[5] mapping  = [ 0,1,2,3,4 ];
279    static immutable int[5] firstmap = [ 0,1,0,5,6 ];
280    immutable(int)* mymap = (y != 0) ? mapping.ptr : firstmap.ptr;
281    int i;
282    int type = mymap[filter_type];
283    ubyte *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y);
284    int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes;
285 
286    int line_bytes = width * n * (is16bit ? 2 : 1);
287 
288    if (type==0) {
289 
290       if (is16bit)
291       {
292           version(LittleEndian)
293           {
294                // 16-bit PNG samples are actually in Big Endian
295                for (int x = 0; x < width*n; ++x)
296                {
297                    line_buffer[x*2  ] = z[x*2+1];
298                    line_buffer[x*2+1] = z[x*2  ];
299                }
300           }
301           else
302               memcpy(line_buffer, z, line_bytes);
303       }
304       else
305       {
306           memcpy(line_buffer, z, line_bytes);
307       }
308       return;
309    }
310 
311    int bytePerSample = n * (is16bit ? 2 : 1);
312    int leftOffset = bytePerSample;
313 
314    // first loop isn't optimized since it's just one pixel
315    for (i = 0; i < bytePerSample; ++i) {
316       switch (type) {
317          case 1: line_buffer[i] = z[i]; break;
318          case 2: line_buffer[i] = cast(byte)(z[i] - z[i-signed_stride]); break;
319          case 3: line_buffer[i] = cast(byte)(z[i] - (z[i-signed_stride]>>1)); break;
320          case 4: line_buffer[i] = cast(byte) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break;
321          case 5: line_buffer[i] = z[i]; break;
322          case 6: line_buffer[i] = z[i]; break;
323          default: assert(0);
324       }
325    }
326    switch (type) {
327       case 1: for (i=bytePerSample; i < line_bytes; ++i) line_buffer[i] = cast(byte)(z[i] - z[i-leftOffset]); break;
328       case 2: for (i=bytePerSample; i < line_bytes; ++i) line_buffer[i] = cast(byte)(z[i] - z[i-signed_stride]); break;
329       case 3: for (i=bytePerSample; i < line_bytes; ++i) line_buffer[i] = cast(byte)(z[i] - ((z[i-leftOffset] + z[i-signed_stride])>>1)); break;
330       case 4: for (i=bytePerSample; i < line_bytes; ++i) line_buffer[i] = cast(byte)(z[i] - stbiw__paeth(z[i-leftOffset], z[i-signed_stride], z[i-signed_stride-leftOffset])); break;
331       case 5: for (i=bytePerSample; i < line_bytes; ++i) line_buffer[i] = cast(byte)(z[i] - (z[i-leftOffset]>>1)); break;
332       case 6: for (i=bytePerSample; i < line_bytes; ++i) line_buffer[i] = cast(byte)(z[i] - stbiw__paeth(z[i-leftOffset], 0,0)); break;
333       default: assert(0);
334    }
335 
336    version(LittleEndian)
337    {
338        if (is16bit)
339        {
340            // swap bytes
341            // 16-bit PNG samples are actually in Big Endian
342            // Note that the whole prediction is happening in the native endianness.
343            // (Gamut stores 16-bit samples in native endianness).
344            for (int x = 0; x < width*n; ++x)
345            {
346                ubyte b = line_buffer[x*2  ];
347                line_buffer[x*2  ] = line_buffer[x*2+1];
348                line_buffer[x*2+1] = b;
349            }
350        }
351    }
352 }
353 
354 public ubyte *stbi_write_png_to_mem(const(ubyte*) pixels, int stride_bytes, int x, int y, int n, int *out_len, bool is16bit)
355 {
356     int force_filter = stbi_write_force_png_filter;
357 
358     static immutable int[5] ctype = [ -1, 0, 4, 2, 6 ];
359     static immutable ubyte[8] sig = [ 137,80,78,71,13,10,26,10 ];
360     ubyte *out_, o, filt, zlib;
361     byte* line_buffer;
362     int j, zlen;
363 
364     int lineBytes = x * n * (is16bit ? 2 : 1);
365 
366     if (force_filter >= 5) 
367     {
368         force_filter = -1;
369     }
370 
371     filt = cast(ubyte *) STBIW_MALLOC((lineBytes + 1) * y); // +1 byte for the filter type
372     if (!filt) 
373         return null;
374     line_buffer = cast(byte *) STBIW_MALLOC(lineBytes); 
375     if (!line_buffer) 
376     { 
377         STBIW_FREE(filt); 
378         return null; 
379     }
380 
381    for (j=0; j < y; ++j) 
382    {
383       int filter_type;
384       if (force_filter > -1) {
385          filter_type = force_filter;
386          stbiw__encode_png_line(cast(ubyte*)(pixels), stride_bytes, x, y, j, n, is16bit, force_filter, line_buffer);
387 
388       } else { // Estimate the best filter by running through all of them:
389          int best_filter = 0, best_filter_val = 0x7fffffff, est, i;
390          for (filter_type = 0; filter_type < 5; filter_type++) {
391             stbiw__encode_png_line(cast(ubyte*)(pixels), stride_bytes, x, y, j, n, is16bit, filter_type, line_buffer);
392 
393             // Estimate the entropy of the line using this filter; the less, the better.
394             est = 0;
395             for (i = 0; i < x*n; ++i) { // Note: adapting that entropy for 16-bit samples wins nothing.
396                est += abs(line_buffer[i]);
397             }
398             if (est < best_filter_val) {
399                best_filter_val = est;
400                best_filter = filter_type;
401             }
402          }
403          if (filter_type != best_filter)   // If the last iteration already got us the best filter, don't redo it
404          {
405             stbiw__encode_png_line(cast(ubyte*)(pixels), stride_bytes, x, y, j, n, is16bit, best_filter, line_buffer);
406             filter_type = best_filter;
407          }
408       }
409       // when we get here, filter_type contains the filter type, and line_buffer contains the data
410       filt[j*(lineBytes+1)] = cast(ubyte) filter_type;
411       STBIW_MEMMOVE(filt+j*(lineBytes+1)+1, line_buffer, lineBytes);
412    }
413    STBIW_FREE(line_buffer);
414    zlib = stbi_zlib_compress(filt, y*(lineBytes+1), &zlen, stbi_write_png_compression_level);
415    STBIW_FREE(filt);
416    if (!zlib) 
417        return null;
418 
419    // each tag requires 12 bytes of overhead
420    out_ = cast(ubyte *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12);
421    if (!out_) return null;
422    *out_len = 8 + 12+13 + 12+zlen + 12;
423 
424    o = out_;
425    STBIW_MEMMOVE(o, sig.ptr, 8); 
426    o+= 8;
427    stbiw__wp32(o, 13); // header length
428    stbiw__wptag(o, "IHDR");
429    stbiw__wp32(o, x);
430    stbiw__wp32(o, y);
431    *o++ = (is16bit ? 16 : 8);
432    *o++ = STBIW_UCHAR(ctype[n]);
433    *o++ = 0;
434    *o++ = 0;
435    *o++ = 0;
436    stbiw__wpcrc(&o,13);
437 
438    stbiw__wp32(o, zlen);
439    stbiw__wptag(o, "IDAT");
440    STBIW_MEMMOVE(o, zlib, zlen);
441    o += zlen;
442    STBIW_FREE(zlib);
443    stbiw__wpcrc(&o, zlen);
444 
445    stbiw__wp32(o,0);
446    stbiw__wptag(o, "IEND");
447    stbiw__wpcrc(&o,0);
448 
449    assert(o == out_ + *out_len);
450 
451    return out_;
452 }
453 
454 
455 
456 // JPEG encoder
457 
458 
459 /* ***************************************************************************
460 *
461 * JPEG writer
462 *
463 * This is based on Jon Olick's jo_jpeg.cpp:
464 * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html
465 */
466 
467 static immutable ubyte[64] stbiw__jpg_ZigZag = 
468 [ 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18,
469 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 ];
470 
471 void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const(ushort)* bs) 
472 {
473     int bitBuf = *bitBufP;
474     int bitCnt = *bitCntP;
475     bitCnt += bs[1];
476     bitBuf |= bs[0] << (24 - bitCnt);
477     while(bitCnt >= 8) 
478     {
479         ubyte c = (bitBuf >> 16) & 255;
480         stbiw__putc(s, c);
481         if(c == 255) {
482             stbiw__putc(s, 0);
483         }
484         bitBuf <<= 8;
485         bitCnt -= 8;
486     }
487     *bitBufP = bitBuf;
488     *bitCntP = bitCnt;
489 }
490 
491 void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) 
492 {
493     float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p;
494     float z1, z2, z3, z4, z5, z11, z13;
495 
496     float tmp0 = d0 + d7;
497     float tmp7 = d0 - d7;
498     float tmp1 = d1 + d6;
499     float tmp6 = d1 - d6;
500     float tmp2 = d2 + d5;
501     float tmp5 = d2 - d5;
502     float tmp3 = d3 + d4;
503     float tmp4 = d3 - d4;
504 
505     // Even part
506     float tmp10 = tmp0 + tmp3;   // phase 2
507     float tmp13 = tmp0 - tmp3;
508     float tmp11 = tmp1 + tmp2;
509     float tmp12 = tmp1 - tmp2;
510 
511     d0 = tmp10 + tmp11;       // phase 3
512     d4 = tmp10 - tmp11;
513 
514     z1 = (tmp12 + tmp13) * 0.707106781f; // c4
515     d2 = tmp13 + z1;       // phase 5
516     d6 = tmp13 - z1;
517 
518     // Odd part
519     tmp10 = tmp4 + tmp5;       // phase 2
520     tmp11 = tmp5 + tmp6;
521     tmp12 = tmp6 + tmp7;
522 
523     // The rotator is modified from fig 4-8 to avoid extra negations.
524     z5 = (tmp10 - tmp12) * 0.382683433f; // c6
525     z2 = tmp10 * 0.541196100f + z5; // c2-c6
526     z4 = tmp12 * 1.306562965f + z5; // c2+c6
527     z3 = tmp11 * 0.707106781f; // c4
528 
529     z11 = tmp7 + z3;      // phase 5
530     z13 = tmp7 - z3;
531 
532     *d5p = z13 + z2;         // phase 6
533     *d3p = z13 - z2;
534     *d1p = z11 + z4;
535     *d7p = z11 - z4;
536 
537     *d0p = d0;  
538     *d2p = d2;  
539     *d4p = d4;  
540     *d6p = d6;
541 }
542 
543 void stbiw__jpg_calcBits(int val, ushort* bits) 
544 {
545     int tmp1 = val < 0 ? -val : val;
546     val = val < 0 ? val-1 : val;
547     bits[1] = 1;
548     while(tmp1 >>= 1) 
549     {
550         ++bits[1];
551     }
552     bits[0] = cast(ushort)(val & ((1<<bits[1])-1));
553 }
554 
555 int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, int *bitCnt, 
556                          float *CDU, int du_stride, float *fdtbl, int DC, 
557                          const(ushort[2])* HTDC, 
558                          const(ushort[2])* HTAC) 
559 {
560     const(ushort[2]) EOB = [ HTAC[0x00][0], HTAC[0x00][1] ];
561     const(ushort[2]) M16zeroes = [ HTAC[0xF0][0], HTAC[0xF0][1] ];
562     int dataOff, i, j, n, diff, end0pos, x, y;
563     int[64] DU;
564 
565     // DCT rows
566     for(dataOff=0, n=du_stride*8; dataOff<n; dataOff+=du_stride) 
567     {
568         stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+1], &CDU[dataOff+2], &CDU[dataOff+3], &CDU[dataOff+4], &CDU[dataOff+5], &CDU[dataOff+6], &CDU[dataOff+7]);
569     }
570     // DCT columns
571     for(dataOff=0; dataOff<8; ++dataOff) 
572     {
573         stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+du_stride], &CDU[dataOff+du_stride*2], &CDU[dataOff+du_stride*3], &CDU[dataOff+du_stride*4],
574                        &CDU[dataOff+du_stride*5], &CDU[dataOff+du_stride*6], &CDU[dataOff+du_stride*7]);
575     }
576     // Quantize/descale/zigzag the coefficients
577     for(y = 0, j=0; y < 8; ++y) 
578     {
579         for(x = 0; x < 8; ++x,++j) 
580         {
581             float v;
582             i = y*du_stride+x;
583             v = CDU[i]*fdtbl[j];
584             // DU[stbiw__jpg_ZigZag[j]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));
585             // ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway?
586             DU[stbiw__jpg_ZigZag[j]] = cast(int)(v < 0 ? v - 0.5f : v + 0.5f);
587         }
588     }
589 
590     // Encode DC
591     diff = DU[0] - DC;
592     if (diff == 0) {
593         stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[0].ptr);
594     } else {
595         ushort[2] bits;
596         stbiw__jpg_calcBits(diff, bits.ptr);
597         stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[bits[1]].ptr);
598         stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits.ptr);
599     }
600     // Encode ACs
601     end0pos = 63;
602     for(; (end0pos>0)&&(DU[end0pos]==0); --end0pos) {
603     }
604     // end0pos = first element in reverse order !=0
605     if(end0pos == 0) {
606         stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB.ptr);
607         return DU[0];
608     }
609     for(i = 1; i <= end0pos; ++i) {
610         int startpos = i;
611         int nrzeroes;
612         ushort[2] bits;
613         for (; DU[i]==0 && i<=end0pos; ++i) {
614         }
615         nrzeroes = i-startpos;
616         if ( nrzeroes >= 16 ) {
617             int lng = nrzeroes>>4;
618             int nrmarker;
619             for (nrmarker=1; nrmarker <= lng; ++nrmarker)
620                 stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes.ptr);
621             nrzeroes &= 15;
622         }
623         stbiw__jpg_calcBits(DU[i], bits.ptr);
624         stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]].ptr);
625         stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits.ptr);
626     }
627     if(end0pos != 63) {
628         stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB.ptr);
629     }
630     return DU[0];
631 }
632 
633 int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) 
634 {
635     // Constants that don't pollute global namespace
636     static immutable ubyte[17] std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0];
637     static immutable ubyte[12] std_dc_luminance_values  = [0,1,2,3,4,5,6,7,8,9,10,11];
638     static immutable ubyte[17] std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d];
639     static immutable ubyte[162] std_ac_luminance_values = [
640         0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12, 0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,
641         0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,
642         0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16, 0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,
643         0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39, 0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
644         0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,
645         0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79, 0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
646         0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98, 0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,
647         0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,
648         0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4, 0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,
649         0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea, 0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
650         0xf9,0xfa
651     ];
652 
653     static immutable ubyte[17] std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0];
654     static immutable ubyte[12] std_dc_chrominance_values  = [0,1,2,3,4,5,6,7,8,9,10,11];
655     static immutable ubyte[17] std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77];
656     static immutable ubyte[162] std_ac_chrominance_values = [
657         0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21, 0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,
658         0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,
659         0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34, 0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,
660         0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38, 0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,
661         0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,
662         0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78, 0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
663         0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96, 0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,
664         0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,
665         0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2, 0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,
666         0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9, 0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
667         0xf9,0xfa
668     ];
669 
670     // Huffman tables
671     static immutable ushort[2][256] YDC_HT = 
672     [ [0,2],[2,3],[3,3],[4,3],[5,3],[6,3],[14,4],[30,5],[62,6],[126,7],[254,8],[510,9] ];
673 
674     static immutable ushort[2][256] UVDC_HT = 
675     [ [0,2],[1,2],[2,2],[6,3],[14,4],[30,5],[62,6],[126,7],[254,8],[510,9],[1022,10],[2046,11]];
676 
677     static immutable ushort[2][256] YAC_HT = 
678     [
679         [10,4],[0,2],[1,2],[4,3],[11,4],[26,5],[120,7],[248,8],[1014,10],[65410,16],[65411,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
680         [12,4],[27,5],[121,7],[502,9],[2038,11],[65412,16],[65413,16],[65414,16],[65415,16],[65416,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
681         [28,5],[249,8],[1015,10],[4084,12],[65417,16],[65418,16],[65419,16],[65420,16],[65421,16],[65422,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
682         [58,6],[503,9],[4085,12],[65423,16],[65424,16],[65425,16],[65426,16],[65427,16],[65428,16],[65429,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
683         [59,6],[1016,10],[65430,16],[65431,16],[65432,16],[65433,16],[65434,16],[65435,16],[65436,16],[65437,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
684         [122,7],[2039,11],[65438,16],[65439,16],[65440,16],[65441,16],[65442,16],[65443,16],[65444,16],[65445,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
685         [123,7],[4086,12],[65446,16],[65447,16],[65448,16],[65449,16],[65450,16],[65451,16],[65452,16],[65453,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
686         [250,8],[4087,12],[65454,16],[65455,16],[65456,16],[65457,16],[65458,16],[65459,16],[65460,16],[65461,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
687         [504,9],[32704,15],[65462,16],[65463,16],[65464,16],[65465,16],[65466,16],[65467,16],[65468,16],[65469,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
688         [505,9],[65470,16],[65471,16],[65472,16],[65473,16],[65474,16],[65475,16],[65476,16],[65477,16],[65478,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
689         [506,9],[65479,16],[65480,16],[65481,16],[65482,16],[65483,16],[65484,16],[65485,16],[65486,16],[65487,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
690         [1017,10],[65488,16],[65489,16],[65490,16],[65491,16],[65492,16],[65493,16],[65494,16],[65495,16],[65496,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
691         [1018,10],[65497,16],[65498,16],[65499,16],[65500,16],[65501,16],[65502,16],[65503,16],[65504,16],[65505,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
692         [2040,11],[65506,16],[65507,16],[65508,16],[65509,16],[65510,16],[65511,16],[65512,16],[65513,16],[65514,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
693         [65515,16],[65516,16],[65517,16],[65518,16],[65519,16],[65520,16],[65521,16],[65522,16],[65523,16],[65524,16],[0,0],[0,0],[0,0],[0,0],[0,0],
694         [2041,11],[65525,16],[65526,16],[65527,16],[65528,16],[65529,16],[65530,16],[65531,16],[65532,16],[65533,16],[65534,16],[0,0],[0,0],[0,0],[0,0],[0,0]
695     ];
696     static immutable ushort[2][256] UVAC_HT = [
697         [0,2],[1,2],[4,3],[10,4],[24,5],[25,5],[56,6],[120,7],[500,9],[1014,10],[4084,12],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
698         [11,4],[57,6],[246,8],[501,9],[2038,11],[4085,12],[65416,16],[65417,16],[65418,16],[65419,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
699         [26,5],[247,8],[1015,10],[4086,12],[32706,15],[65420,16],[65421,16],[65422,16],[65423,16],[65424,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
700         [27,5],[248,8],[1016,10],[4087,12],[65425,16],[65426,16],[65427,16],[65428,16],[65429,16],[65430,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
701         [58,6],[502,9],[65431,16],[65432,16],[65433,16],[65434,16],[65435,16],[65436,16],[65437,16],[65438,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
702         [59,6],[1017,10],[65439,16],[65440,16],[65441,16],[65442,16],[65443,16],[65444,16],[65445,16],[65446,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
703         [121,7],[2039,11],[65447,16],[65448,16],[65449,16],[65450,16],[65451,16],[65452,16],[65453,16],[65454,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
704         [122,7],[2040,11],[65455,16],[65456,16],[65457,16],[65458,16],[65459,16],[65460,16],[65461,16],[65462,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
705         [249,8],[65463,16],[65464,16],[65465,16],[65466,16],[65467,16],[65468,16],[65469,16],[65470,16],[65471,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
706         [503,9],[65472,16],[65473,16],[65474,16],[65475,16],[65476,16],[65477,16],[65478,16],[65479,16],[65480,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
707         [504,9],[65481,16],[65482,16],[65483,16],[65484,16],[65485,16],[65486,16],[65487,16],[65488,16],[65489,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
708         [505,9],[65490,16],[65491,16],[65492,16],[65493,16],[65494,16],[65495,16],[65496,16],[65497,16],[65498,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
709         [506,9],[65499,16],[65500,16],[65501,16],[65502,16],[65503,16],[65504,16],[65505,16],[65506,16],[65507,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
710         [2041,11],[65508,16],[65509,16],[65510,16],[65511,16],[65512,16],[65513,16],[65514,16],[65515,16],[65516,16],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],
711         [16352,14],[65517,16],[65518,16],[65519,16],[65520,16],[65521,16],[65522,16],[65523,16],[65524,16],[65525,16],[0,0],[0,0],[0,0],[0,0],[0,0],
712         [1018,10],[32707,15],[65526,16],[65527,16],[65528,16],[65529,16],[65530,16],[65531,16],[65532,16],[65533,16],[65534,16],[0,0],[0,0],[0,0],[0,0],[0,0]
713     ];
714     static immutable int[] YQT = [16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22,
715     37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99];
716 
717     static immutable int[] UVQT = [17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99,
718     99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99];
719 
720     static immutable float[] aasf = [ 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f,
721     1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f ];
722 
723     int row, col, i, k, subsample;
724     float[64] fdtbl_Y, fdtbl_UV;
725     ubyte[64] YTable, UVTable;
726 
727     if(!data || !width || !height || comp > 4 || comp < 1) {
728         return 0;
729     }
730 
731     quality = quality ? quality : 90;
732     subsample = quality <= 90 ? 1 : 0;
733     quality = quality < 1 ? 1 : quality > 100 ? 100 : quality;
734     quality = quality < 50 ? 5000 / quality : 200 - quality * 2;
735 
736     for(i = 0; i < 64; ++i) {
737         int uvti, yti = (YQT[i]*quality+50)/100;
738         YTable[stbiw__jpg_ZigZag[i]] = cast(ubyte) (yti < 1 ? 1 : yti > 255 ? 255 : yti);
739         uvti = (UVQT[i]*quality+50)/100;
740         UVTable[stbiw__jpg_ZigZag[i]] = cast(ubyte) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti);
741     }
742 
743     for(row = 0, k = 0; row < 8; ++row) {
744         for(col = 0; col < 8; ++col, ++k) {
745             fdtbl_Y[k]  = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);
746             fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);
747         }
748     }
749 
750     // Write Headers
751     {
752         static immutable ubyte[] head0 = [ 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 ];
753         static immutable ubyte[] head2 = [ 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 ];
754         immutable ubyte[24] head1 = 
755         [ 
756             0xFF, 0xC0, 0, 0x11, 8, 
757             cast(ubyte)(height>>8),
758             cast(ubyte)(height),
759             cast(ubyte)(width>>8),
760             cast(ubyte)(width),
761         3,1,cast(ubyte)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 ];
762 
763 
764         s.func(s.context, head0.ptr, cast(int)head0.length);
765         s.func(s.context, YTable.ptr, cast(int)YTable.length);
766         stbiw__putc(s, 1);
767         s.func(s.context, UVTable.ptr, cast(int)UVTable.length);
768         s.func(s.context, head1.ptr, cast(int)head1.sizeof);
769         s.func(s.context, (std_dc_luminance_nrcodes.ptr+1), cast(int)std_dc_luminance_nrcodes.length - 1);
770         s.func(s.context, std_dc_luminance_values.ptr, cast(int)std_dc_luminance_values.length);
771         stbiw__putc(s, 0x10); // HTYACinfo
772         s.func(s.context, (std_ac_luminance_nrcodes.ptr+1), cast(int)std_ac_luminance_nrcodes.length-1);
773         s.func(s.context, std_ac_luminance_values.ptr, cast(int)std_ac_luminance_values.length);
774         stbiw__putc(s, 1); // HTUDCinfo
775         s.func(s.context, (std_dc_chrominance_nrcodes.ptr+1), cast(int)std_dc_chrominance_nrcodes.length-1);
776         s.func(s.context, std_dc_chrominance_values.ptr, cast(int)std_dc_chrominance_values.length);
777         stbiw__putc(s, 0x11); // HTUACinfo
778         s.func(s.context, (std_ac_chrominance_nrcodes.ptr+1), cast(int)std_ac_chrominance_nrcodes.length-1);
779         s.func(s.context, std_ac_chrominance_values.ptr, cast(int)std_ac_chrominance_values.length);
780         s.func(s.context, head2.ptr, cast(int) head2.length);
781     }
782 
783     // Encode 8x8 macroblocks
784     {
785         static immutable ushort[2] fillBits = [0x7F, 7];
786         int DCY=0, DCU=0, DCV=0;
787         int bitBuf=0, bitCnt=0;
788         // comp == 2 is grey+alpha (alpha is ignored)
789         int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0;
790         const(ubyte)* dataR = cast(const(ubyte)*)data;
791         const(ubyte)* dataG = dataR + ofsG;
792         const(ubyte)* dataB = dataR + ofsB;
793         int x, y, pos;
794         if(subsample) {
795             for(y = 0; y < height; y += 16) {
796                 for(x = 0; x < width; x += 16) {
797                     float[256] Y = void;
798                     float[256] U = void;
799                     float[256] V = void;
800                     for(row = y, pos = 0; row < y+16; ++row) {
801                         // row >= height => use last input row
802                         int clamped_row = (row < height) ? row : height - 1;
803                         int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;
804                         for(col = x; col < x+16; ++col, ++pos) {
805                             // if col >= width => use pixel from last input column
806                             int p = base_p + ((col < width) ? col : (width-1))*comp;
807                             float r = dataR[p], g = dataG[p], b = dataB[p];
808                             Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128;
809                             U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b;
810                             V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b;
811                         }
812                     }
813                     DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y.ptr+0,   16, fdtbl_Y.ptr, DCY, YDC_HT.ptr, YAC_HT.ptr);
814                     DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y.ptr+8,   16, fdtbl_Y.ptr, DCY, YDC_HT.ptr, YAC_HT.ptr);
815                     DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y.ptr+128, 16, fdtbl_Y.ptr, DCY, YDC_HT.ptr, YAC_HT.ptr);
816                     DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y.ptr+136, 16, fdtbl_Y.ptr, DCY, YDC_HT.ptr, YAC_HT.ptr);
817 
818                     // subsample U,V
819                     {
820                         float[64] subU = void;
821                         float[64] subV = void;
822                         int yy, xx;
823                         for(yy = 0, pos = 0; yy < 8; ++yy) {
824                             for(xx = 0; xx < 8; ++xx, ++pos) {
825                                 int j = yy*32+xx*2;
826                                 subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f;
827                                 subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f;
828                             }
829                         }
830                         DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU.ptr, 8, fdtbl_UV.ptr, DCU, UVDC_HT.ptr, UVAC_HT.ptr);
831                         DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV.ptr, 8, fdtbl_UV.ptr, DCV, UVDC_HT.ptr, UVAC_HT.ptr);
832                     }
833                 }
834             }
835         } else {
836             for(y = 0; y < height; y += 8) {
837                 for(x = 0; x < width; x += 8) {
838                     float[64] Y = void;
839                     float[64] U = void;
840                     float[64] V = void;                    
841                     for(row = y, pos = 0; row < y+8; ++row) {
842                         // row >= height => use last input row
843                         int clamped_row = (row < height) ? row : height - 1;
844                         int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp;
845                         for(col = x; col < x+8; ++col, ++pos) {
846                             // if col >= width => use pixel from last input column
847                             int p = base_p + ((col < width) ? col : (width-1))*comp;
848                             float r = dataR[p], g = dataG[p], b = dataB[p];
849                             Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128;
850                             U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b;
851                             V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b;
852                         }
853                     }
854 
855                     DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y.ptr, 8, fdtbl_Y.ptr,  DCY, YDC_HT.ptr, YAC_HT.ptr);
856                     DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U.ptr, 8, fdtbl_UV.ptr, DCU, UVDC_HT.ptr, UVAC_HT.ptr);
857                     DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V.ptr, 8, fdtbl_UV.ptr, DCV, UVDC_HT.ptr, UVAC_HT.ptr);
858                 }
859             }
860         }
861 
862         // Do the bit alignment of the EOI marker
863         stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits.ptr);
864     }
865 
866     // EOI
867     stbiw__putc(s, 0xFF);
868     stbiw__putc(s, 0xD9);
869 
870     return 1;
871 }
872 
873 public int stbi_write_jpg_to_func(stbi_write_func func, void *context, int x, int y, int comp, const(void) *data, int quality)
874 {
875     stbi__write_context s;
876     stbi__start_write_callbacks(&s, func, context);
877     return stbi_write_jpg_core(&s, x, y, comp, cast(void *) data, quality); // const_cast here
878 }
879 
880 
881 /* Revision history
882       1.16  (2021-07-11)
883              make Deflate code emit uncompressed blocks when it would otherwise expand
884              support writing BMPs with alpha channel
885       1.15  (2020-07-13) unknown
886       1.14  (2020-02-02) updated JPEG writer to downsample chroma channels
887       1.13
888       1.12
889       1.11  (2019-08-11)
890 
891       1.10  (2019-02-07)
892              support utf8 filenames in Windows; fix warnings and platform ifdefs
893       1.09  (2018-02-11)
894              fix typo in zlib quality API, improve STB_I_W_STATIC in C++
895       1.08  (2018-01-29)
896              add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter
897       1.07  (2017-07-24)
898              doc fix
899       1.06 (2017-07-23)
900              writing JPEG (using Jon Olick's code)
901       1.05   ???
902       1.04 (2017-03-03)
903              monochrome BMP expansion
904       1.03   ???
905       1.02 (2016-04-02)
906              avoid allocating large structures on the stack
907       1.01 (2016-01-16)
908              STBIW_REALLOC_SIZED: support allocators with no realloc support
909              avoid race-condition in crc initialization
910              minor compile issues
911       1.00 (2015-09-14)
912              installable file IO function
913       0.99 (2015-09-13)
914              warning fixes; TGA rle support
915       0.98 (2015-04-08)
916              added STBIW_MALLOC, STBIW_ASSERT etc
917       0.97 (2015-01-18)
918              fixed HDR asserts, rewrote HDR rle logic
919       0.96 (2015-01-17)
920              add HDR output
921              fix monochrome BMP
922       0.95 (2014-08-17)
923              add monochrome TGA output
924       0.94 (2014-05-31)
925              rename private functions to avoid conflicts with stb_image.h
926       0.93 (2014-05-27)
927              warning fixes
928       0.92 (2010-08-01)
929              casts to unsigned char to fix warnings
930       0.91 (2010-07-17)
931              first public release
932       0.90   first internal release
933 */
934 
935 /*
936 ------------------------------------------------------------------------------
937 This software is available under 2 licenses -- choose whichever you prefer.
938 ------------------------------------------------------------------------------
939 ALTERNATIVE A - MIT License
940 Copyright (c) 2017 Sean Barrett
941 Permission is hereby granted, free of charge, to any person obtaining a copy of
942 this software and associated documentation files (the "Software"), to deal in
943 the Software without restriction, including without limitation the rights to
944 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
945 of the Software, and to permit persons to whom the Software is furnished to do
946 so, subject to the following conditions:
947 The above copyright notice and this permission notice shall be included in all
948 copies or substantial portions of the Software.
949 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
950 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
951 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
952 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
953 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
954 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
955 SOFTWARE.
956 ------------------------------------------------------------------------------
957 ALTERNATIVE B - Public Domain (www.unlicense.org)
958 This is free and unencumbered software released into the public domain.
959 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
960 software, either in source code form or as a compiled binary, for any purpose,
961 commercial or non-commercial, and by any means.
962 In jurisdictions that recognize copyright laws, the author or authors of this
963 software dedicate any and all copyright interest in the software to the public
964 domain. We make this dedication for the benefit of the public at large and to
965 the detriment of our heirs and successors. We intend this dedication to be an
966 overt act of relinquishment in perpetuity of all present and future rights to
967 this software under copyright law.
968 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
969 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
970 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
971 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
972 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
973 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
974 ------------------------------------------------------------------------------
975 */