1 /* stb_image_write - v1.01 - public domain - http://nothings.org/stb/stb_image_write.h
2 writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015
3 no warranty implied; use at your own risk
4
5 Before #including,
6
7 #define STB_IMAGE_WRITE_IMPLEMENTATION
8
9 in the file that you want to have the implementation.
10
11 Will probably not work correctly with strict-aliasing optimizations.
12
13 ABOUT:
14
15 This header file is a library for writing images to C stdio. It could be
16 adapted to write to memory or a general streaming interface; let me know.
17
18 The PNG output is not optimal; it is 20-50% larger than the file
19 written by a decent optimizing implementation. This library is designed
20 for source code compactness and simplicity, not optimal image file size
21 or run-time performance.
22
23 BUILDING:
24
25 You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h.
26 You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace
27 malloc,realloc,free.
28 You can define STBIW_MEMMOVE() to replace memmove()
29
30 USAGE:
31
32 There are four functions, one for each image file format:
33
34 int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
35 int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
36 int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
37 int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
38
39 There are also four equivalent functions that use an arbitrary write function. You are
40 expected to open/close your file-equivalent before and after calling these:
41
42 int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes);
43 int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
44 int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
45 int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);
46
47 where the callback is:
48 void stbi_write_func(void *context, void *data, int size);
49
50 You can define STBI_WRITE_NO_STDIO to disable the file variant of these
51 functions, so the library will not use stdio.h at all. However, this will
52 also disable HDR writing, because it requires stdio for formatted output.
53
54 Each function returns 0 on failure and non-0 on success.
55
56 The functions create an image file defined by the parameters. The image
57 is a rectangle of pixels stored from left-to-right, top-to-bottom.
58 Each pixel contains 'comp' channels of data stored interleaved with 8-bits
59 per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is
60 monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall.
61 The *data pointer points to the first byte of the top-left-most pixel.
62 For PNG, "stride_in_bytes" is the distance in bytes from the first byte of
63 a row of pixels to the first byte of the next row of pixels.
64
65 PNG creates output files with the same number of components as the input.
66 The BMP format expands Y to RGB in the file format and does not
67 output alpha.
68
69 PNG supports writing rectangles of data even when the bytes storing rows of
70 data are not consecutive in memory (e.g. sub-rectangles of a larger image),
71 by supplying the stride between the beginning of adjacent rows. The other
72 formats do not. (Thus you cannot write a native-format BMP through the BMP
73 writer, both because it is in BGR order and because it may have padding
74 at the end of the line.)
75
76 HDR expects linear float data. Since the format is always 32-bit rgb(e)
77 data, alpha (if provided) is discarded, and for monochrome data it is
78 replicated across all three channels.
79
80 TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed
81 data, set the global variable 'stbi_write_tga_with_rle' to 0.
82
83 CREDITS:
84
85 PNG/BMP/TGA
86 Sean Barrett
87 HDR
88 Baldur Karlsson
89 TGA monochrome:
90 Jean-Sebastien Guay
91 misc enhancements:
92 Tim Kelsey
93 TGA RLE
94 Alan Hickman
95 initial file IO callback implementation
96 Emmanuel Julien
97 bugfixes:
98 github:Chribba
99 Guillaume Chereau
100 github:jry2
101 github:romigrou
102 Sergio Gonzalez
103 Jonas Karlsson
104 Filip Wasil
105
106 LICENSE
107
108 This software is in the public domain. Where that dedication is not
109 recognized, you are granted a perpetual, irrevocable license to copy,
110 distribute, and modify this file as you see fit.
111
112 */
113
114 #ifndef INCLUDE_STB_IMAGE_WRITE_H
115 #define INCLUDE_STB_IMAGE_WRITE_H
116
117 #ifdef __cplusplus
118 extern "C" {
119 #endif
120
121 #ifdef STB_IMAGE_WRITE_STATIC
122 #define STBIWDEF static
123 #else
124 #define STBIWDEF extern
125 extern int stbi_write_tga_with_rle;
126 #endif
127
128 #ifndef STBI_WRITE_NO_STDIO
129 STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
130 STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
131 STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
132 STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
133 #endif
134
135 typedef void stbi_write_func(void *context, void *data, int size);
136
137 STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes);
138 STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
139 STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data);
140 STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);
141
142 #ifdef __cplusplus
143 }
144 #endif
145
146 #endif//INCLUDE_STB_IMAGE_WRITE_H
147
148 #ifdef STB_IMAGE_WRITE_IMPLEMENTATION
149
150 #ifdef _WIN32
151 #ifndef _CRT_SECURE_NO_WARNINGS
152 #define _CRT_SECURE_NO_WARNINGS
153 #endif
154 #ifndef _CRT_NONSTDC_NO_DEPRECATE
155 #define _CRT_NONSTDC_NO_DEPRECATE
156 #endif
157 #endif
158
159 #ifndef STBI_WRITE_NO_STDIO
160 #include <stdio.h>
161 #endif // STBI_WRITE_NO_STDIO
162
163 #include <stdarg.h>
164 #include <stdlib.h>
165 #include <string.h>
166 #include <math.h>
167
168 #if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED))
169 // ok
170 #elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED)
171 // ok
172 #else
173 #error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)."
174 #endif
175
176 #ifndef STBIW_MALLOC
177 #define STBIW_MALLOC(sz) malloc(sz)
178 #define STBIW_REALLOC(p,newsz) realloc(p,newsz)
179 #define STBIW_FREE(p) free(p)
180 #endif
181
182 #ifndef STBIW_REALLOC_SIZED
183 #define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz)
184 #endif
185
186
187 #ifndef STBIW_MEMMOVE
188 #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz)
189 #endif
190
191
192 #ifndef STBIW_ASSERT
193 #include <assert.h>
194 #define STBIW_ASSERT(x) assert(x)
195 #endif
196
197 #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff)
198
199 typedef struct
200 {
201 stbi_write_func *func;
202 void *context;
203 } stbi__write_context;
204
205 // initialize a callback-based context
stbi__start_write_callbacks(stbi__write_context * s,stbi_write_func * c,void * context)206 static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context)
207 {
208 s->func = c;
209 s->context = context;
210 }
211
212 #ifndef STBI_WRITE_NO_STDIO
213
stbi__stdio_write(void * context,void * data,int size)214 static void stbi__stdio_write(void *context, void *data, int size)
215 {
216 fwrite(data,1,size,(FILE*) context);
217 }
218
stbi__start_write_file(stbi__write_context * s,const char * filename)219 static int stbi__start_write_file(stbi__write_context *s, const char *filename)
220 {
221 FILE *f = fopen(filename, "wb");
222 stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f);
223 return f != NULL;
224 }
225
stbi__end_write_file(stbi__write_context * s)226 static void stbi__end_write_file(stbi__write_context *s)
227 {
228 fclose((FILE *)s->context);
229 }
230
231 #endif // !STBI_WRITE_NO_STDIO
232
233 typedef unsigned int stbiw_uint32;
234 typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];
235
236 #ifdef STB_IMAGE_WRITE_STATIC
237 static int stbi_write_tga_with_rle = 1;
238 #else
239 int stbi_write_tga_with_rle = 1;
240 #endif
241
stbiw__writefv(stbi__write_context * s,const char * fmt,va_list v)242 static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v)
243 {
244 while (*fmt) {
245 switch (*fmt++) {
246 case ' ': break;
247 case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int));
248 s->func(s->context,&x,1);
249 break; }
250 case '2': { int x = va_arg(v,int);
251 unsigned char b[2];
252 b[0] = STBIW_UCHAR(x);
253 b[1] = STBIW_UCHAR(x>>8);
254 s->func(s->context,b,2);
255 break; }
256 case '4': { stbiw_uint32 x = va_arg(v,int);
257 unsigned char b[4];
258 b[0]=STBIW_UCHAR(x);
259 b[1]=STBIW_UCHAR(x>>8);
260 b[2]=STBIW_UCHAR(x>>16);
261 b[3]=STBIW_UCHAR(x>>24);
262 s->func(s->context,b,4);
263 break; }
264 default:
265 STBIW_ASSERT(0);
266 return;
267 }
268 }
269 }
270
stbiw__writef(stbi__write_context * s,const char * fmt,...)271 static void stbiw__writef(stbi__write_context *s, const char *fmt, ...)
272 {
273 va_list v;
274 va_start(v, fmt);
275 stbiw__writefv(s, fmt, v);
276 va_end(v);
277 }
278
stbiw__write3(stbi__write_context * s,unsigned char a,unsigned char b,unsigned char c)279 static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c)
280 {
281 unsigned char arr[3];
282 arr[0] = a, arr[1] = b, arr[2] = c;
283 s->func(s->context, arr, 3);
284 }
285
stbiw__write_pixel(stbi__write_context * s,int rgb_dir,int comp,int write_alpha,int expand_mono,unsigned char * d)286 static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d)
287 {
288 unsigned char bg[3] = { 255, 0, 255}, px[3];
289 int k;
290
291 if (write_alpha < 0)
292 s->func(s->context, &d[comp - 1], 1);
293
294 switch (comp) {
295 case 1:
296 s->func(s->context,d,1);
297 break;
298 case 2:
299 if (expand_mono)
300 stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp
301 else
302 s->func(s->context, d, 1); // monochrome TGA
303 break;
304 case 4:
305 if (!write_alpha) {
306 // composite against pink background
307 for (k = 0; k < 3; ++k)
308 px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255;
309 stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]);
310 break;
311 }
312 /* FALLTHROUGH */
313 case 3:
314 stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]);
315 break;
316 }
317 if (write_alpha > 0)
318 s->func(s->context, &d[comp - 1], 1);
319 }
320
stbiw__write_pixels(stbi__write_context * s,int rgb_dir,int vdir,int x,int y,int comp,void * data,int write_alpha,int scanline_pad,int expand_mono)321 static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono)
322 {
323 stbiw_uint32 zero = 0;
324 int i,j, j_end;
325
326 if (y <= 0)
327 return;
328
329 if (vdir < 0)
330 j_end = -1, j = y-1;
331 else
332 j_end = y, j = 0;
333
334 for (; j != j_end; j += vdir) {
335 for (i=0; i < x; ++i) {
336 unsigned char *d = (unsigned char *) data + (j*x+i)*comp;
337 stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d);
338 }
339 s->func(s->context, &zero, scanline_pad);
340 }
341 }
342
stbiw__outfile(stbi__write_context * s,int rgb_dir,int vdir,int x,int y,int comp,int expand_mono,void * data,int alpha,int pad,const char * fmt,...)343 static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...)
344 {
345 if (y < 0 || x < 0) {
346 return 0;
347 } else {
348 va_list v;
349 va_start(v, fmt);
350 stbiw__writefv(s, fmt, v);
351 va_end(v);
352 stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono);
353 return 1;
354 }
355 }
356
stbi_write_bmp_core(stbi__write_context * s,int x,int y,int comp,const void * data)357 static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data)
358 {
359 int pad = (-x*3) & 3;
360 return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad,
361 "11 4 22 4" "4 44 22 444444",
362 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header
363 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header
364 }
365
stbi_write_bmp_to_func(stbi_write_func * func,void * context,int x,int y,int comp,const void * data)366 STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)
367 {
368 stbi__write_context s;
369 stbi__start_write_callbacks(&s, func, context);
370 return stbi_write_bmp_core(&s, x, y, comp, data);
371 }
372
373 #ifndef STBI_WRITE_NO_STDIO
stbi_write_bmp(char const * filename,int x,int y,int comp,const void * data)374 STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)
375 {
376 stbi__write_context s;
377 if (stbi__start_write_file(&s,filename)) {
378 int r = stbi_write_bmp_core(&s, x, y, comp, data);
379 stbi__end_write_file(&s);
380 return r;
381 } else
382 return 0;
383 }
384 #endif //!STBI_WRITE_NO_STDIO
385
stbi_write_tga_core(stbi__write_context * s,int x,int y,int comp,void * data)386 static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data)
387 {
388 int has_alpha = (comp == 2 || comp == 4);
389 int colorbytes = has_alpha ? comp-1 : comp;
390 int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3
391
392 if (y < 0 || x < 0)
393 return 0;
394
395 if (!stbi_write_tga_with_rle) {
396 return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0,
397 "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8);
398 } else {
399 int i,j,k;
400
401 stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8);
402
403 for (j = y - 1; j >= 0; --j) {
404 unsigned char *row = (unsigned char *) data + j * x * comp;
405 int len;
406
407 for (i = 0; i < x; i += len) {
408 unsigned char *begin = row + i * comp;
409 int diff = 1;
410 len = 1;
411
412 if (i < x - 1) {
413 ++len;
414 diff = memcmp(begin, row + (i + 1) * comp, comp);
415 if (diff) {
416 const unsigned char *prev = begin;
417 for (k = i + 2; k < x && len < 128; ++k) {
418 if (memcmp(prev, row + k * comp, comp)) {
419 prev += comp;
420 ++len;
421 } else {
422 --len;
423 break;
424 }
425 }
426 } else {
427 for (k = i + 2; k < x && len < 128; ++k) {
428 if (!memcmp(begin, row + k * comp, comp)) {
429 ++len;
430 } else {
431 break;
432 }
433 }
434 }
435 }
436
437 if (diff) {
438 unsigned char header = STBIW_UCHAR(len - 1);
439 s->func(s->context, &header, 1);
440 for (k = 0; k < len; ++k) {
441 stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp);
442 }
443 } else {
444 unsigned char header = STBIW_UCHAR(len - 129);
445 s->func(s->context, &header, 1);
446 stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin);
447 }
448 }
449 }
450 }
451 return 1;
452 }
453
stbi_write_tga_to_func(stbi_write_func * func,void * context,int x,int y,int comp,const void * data)454 int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)
455 {
456 stbi__write_context s;
457 stbi__start_write_callbacks(&s, func, context);
458 return stbi_write_tga_core(&s, x, y, comp, (void *) data);
459 }
460
461 #ifndef STBI_WRITE_NO_STDIO
stbi_write_tga(char const * filename,int x,int y,int comp,const void * data)462 int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)
463 {
464 stbi__write_context s;
465 if (stbi__start_write_file(&s,filename)) {
466 int r = stbi_write_tga_core(&s, x, y, comp, (void *) data);
467 stbi__end_write_file(&s);
468 return r;
469 } else
470 return 0;
471 }
472 #endif
473
474 // *************************************************************************************************
475 // Radiance RGBE HDR writer
476 // by Baldur Karlsson
477 #ifndef STBI_WRITE_NO_STDIO
478
479 #define stbiw__max(a, b) ((a) > (b) ? (a) : (b))
480
stbiw__linear_to_rgbe(unsigned char * rgbe,float * linear)481 void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)
482 {
483 int exponent;
484 float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2]));
485
486 if (maxcomp < 1e-32f) {
487 rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
488 } else {
489 float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp;
490
491 rgbe[0] = (unsigned char)(linear[0] * normalize);
492 rgbe[1] = (unsigned char)(linear[1] * normalize);
493 rgbe[2] = (unsigned char)(linear[2] * normalize);
494 rgbe[3] = (unsigned char)(exponent + 128);
495 }
496 }
497
stbiw__write_run_data(stbi__write_context * s,int length,unsigned char databyte)498 void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte)
499 {
500 unsigned char lengthbyte = STBIW_UCHAR(length+128);
501 STBIW_ASSERT(length+128 <= 255);
502 s->func(s->context, &lengthbyte, 1);
503 s->func(s->context, &databyte, 1);
504 }
505
stbiw__write_dump_data(stbi__write_context * s,int length,unsigned char * data)506 void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data)
507 {
508 unsigned char lengthbyte = STBIW_UCHAR(length);
509 STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code
510 s->func(s->context, &lengthbyte, 1);
511 s->func(s->context, data, length);
512 }
513
stbiw__write_hdr_scanline(stbi__write_context * s,int width,int ncomp,unsigned char * scratch,float * scanline)514 void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline)
515 {
516 unsigned char scanlineheader[4] = { 2, 2, 0, 0 };
517 unsigned char rgbe[4];
518 float linear[3];
519 int x;
520
521 scanlineheader[2] = (width&0xff00)>>8;
522 scanlineheader[3] = (width&0x00ff);
523
524 /* skip RLE for images too small or large */
525 if (width < 8 || width >= 32768) {
526 for (x=0; x < width; x++) {
527 switch (ncomp) {
528 case 4: /* fallthrough */
529 case 3: linear[2] = scanline[x*ncomp + 2];
530 linear[1] = scanline[x*ncomp + 1];
531 linear[0] = scanline[x*ncomp + 0];
532 break;
533 default:
534 linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
535 break;
536 }
537 stbiw__linear_to_rgbe(rgbe, linear);
538 s->func(s->context, rgbe, 4);
539 }
540 } else {
541 int c,r;
542 /* encode into scratch buffer */
543 for (x=0; x < width; x++) {
544 switch(ncomp) {
545 case 4: /* fallthrough */
546 case 3: linear[2] = scanline[x*ncomp + 2];
547 linear[1] = scanline[x*ncomp + 1];
548 linear[0] = scanline[x*ncomp + 0];
549 break;
550 default:
551 linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
552 break;
553 }
554 stbiw__linear_to_rgbe(rgbe, linear);
555 scratch[x + width*0] = rgbe[0];
556 scratch[x + width*1] = rgbe[1];
557 scratch[x + width*2] = rgbe[2];
558 scratch[x + width*3] = rgbe[3];
559 }
560
561 s->func(s->context, scanlineheader, 4);
562
563 /* RLE each component separately */
564 for (c=0; c < 4; c++) {
565 unsigned char *comp = &scratch[width*c];
566
567 x = 0;
568 while (x < width) {
569 // find first run
570 r = x;
571 while (r+2 < width) {
572 if (comp[r] == comp[r+1] && comp[r] == comp[r+2])
573 break;
574 ++r;
575 }
576 if (r+2 >= width)
577 r = width;
578 // dump up to first run
579 while (x < r) {
580 int len = r-x;
581 if (len > 128) len = 128;
582 stbiw__write_dump_data(s, len, &comp[x]);
583 x += len;
584 }
585 // if there's a run, output it
586 if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd
587 // find next byte after run
588 while (r < width && comp[r] == comp[x])
589 ++r;
590 // output run up to r
591 while (x < r) {
592 int len = r-x;
593 if (len > 127) len = 127;
594 stbiw__write_run_data(s, len, comp[x]);
595 x += len;
596 }
597 }
598 }
599 }
600 }
601 }
602
stbi_write_hdr_core(stbi__write_context * s,int x,int y,int comp,float * data)603 static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data)
604 {
605 if (y <= 0 || x <= 0 || data == NULL)
606 return 0;
607 else {
608 // Each component is stored separately. Allocate scratch space for full output scanline.
609 unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4);
610 int i, len;
611 char buffer[128];
612 char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n";
613 s->func(s->context, header, sizeof(header)-1);
614
615 len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
616 s->func(s->context, buffer, len);
617
618 for(i=0; i < y; i++)
619 stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x);
620 STBIW_FREE(scratch);
621 return 1;
622 }
623 }
624
stbi_write_hdr_to_func(stbi_write_func * func,void * context,int x,int y,int comp,const float * data)625 int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data)
626 {
627 stbi__write_context s;
628 stbi__start_write_callbacks(&s, func, context);
629 return stbi_write_hdr_core(&s, x, y, comp, (float *) data);
630 }
631
stbi_write_hdr(char const * filename,int x,int y,int comp,const float * data)632 int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data)
633 {
634 stbi__write_context s;
635 if (stbi__start_write_file(&s,filename)) {
636 int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data);
637 stbi__end_write_file(&s);
638 return r;
639 } else
640 return 0;
641 }
642 #endif // STBI_WRITE_NO_STDIO
643
644
645 //////////////////////////////////////////////////////////////////////////////
646 //
647 // PNG writer
648 //
649
650 // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size()
651 #define stbiw__sbraw(a) ((int *) (a) - 2)
652 #define stbiw__sbm(a) stbiw__sbraw(a)[0]
653 #define stbiw__sbn(a) stbiw__sbraw(a)[1]
654
655 #define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a))
656 #define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0)
657 #define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a)))
658
659 #define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v))
660 #define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0)
661 #define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0)
662
stbiw__sbgrowf(void ** arr,int increment,int itemsize)663 static void *stbiw__sbgrowf(void **arr, int increment, int itemsize)
664 {
665 int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1;
666 void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2);
667 STBIW_ASSERT(p);
668 if (p) {
669 if (!*arr) ((int *) p)[1] = 0;
670 *arr = (void *) ((int *) p + 2);
671 stbiw__sbm(*arr) = m;
672 }
673 return *arr;
674 }
675
stbiw__zlib_flushf(unsigned char * data,unsigned int * bitbuffer,int * bitcount)676 static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)
677 {
678 while (*bitcount >= 8) {
679 stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer));
680 *bitbuffer >>= 8;
681 *bitcount -= 8;
682 }
683 return data;
684 }
685
stbiw__zlib_bitrev(int code,int codebits)686 static int stbiw__zlib_bitrev(int code, int codebits)
687 {
688 int res=0;
689 while (codebits--) {
690 res = (res << 1) | (code & 1);
691 code >>= 1;
692 }
693 return res;
694 }
695
stbiw__zlib_countm(unsigned char * a,unsigned char * b,int limit)696 static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit)
697 {
698 int i;
699 for (i=0; i < limit && i < 258; ++i)
700 if (a[i] != b[i]) break;
701 return i;
702 }
703
stbiw__zhash(unsigned char * data)704 static unsigned int stbiw__zhash(unsigned char *data)
705 {
706 stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16);
707 hash ^= hash << 3;
708 hash += hash >> 5;
709 hash ^= hash << 4;
710 hash += hash >> 17;
711 hash ^= hash << 25;
712 hash += hash >> 6;
713 return hash;
714 }
715
716 #define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount))
717 #define stbiw__zlib_add(code,codebits) \
718 (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush())
719 #define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c)
720 // default huffman tables
721 #define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8)
722 #define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9)
723 #define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7)
724 #define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8)
725 #define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n))
726 #define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n))
727
728 #define stbiw__ZHASH 16384
729
stbi_zlib_compress(unsigned char * data,int data_len,int * out_len,int quality)730 unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)
731 {
732 static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 };
733 static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 };
734 static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 };
735 static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };
736 unsigned int bitbuf=0;
737 int i,j, bitcount=0;
738 unsigned char *out = NULL;
739 unsigned char **hash_table[stbiw__ZHASH]; // 64KB on the stack!
740 if (quality < 5) quality = 5;
741
742 stbiw__sbpush(out, 0x78); // DEFLATE 32K window
743 stbiw__sbpush(out, 0x5e); // FLEVEL = 1
744 stbiw__zlib_add(1,1); // BFINAL = 1
745 stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman
746
747 for (i=0; i < stbiw__ZHASH; ++i)
748 hash_table[i] = NULL;
749
750 i=0;
751 while (i < data_len-3) {
752 // hash next 3 bytes of data to be compressed
753 int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3;
754 unsigned char *bestloc = 0;
755 unsigned char **hlist = hash_table[h];
756 int n = stbiw__sbcount(hlist);
757 for (j=0; j < n; ++j) {
758 if (hlist[j]-data > i-32768) { // if entry lies within window
759 int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i);
760 if (d >= best) best=d,bestloc=hlist[j];
761 }
762 }
763 // when hash table entry is too long, delete half the entries
764 if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) {
765 STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);
766 stbiw__sbn(hash_table[h]) = quality;
767 }
768 stbiw__sbpush(hash_table[h],data+i);
769
770 if (bestloc) {
771 // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal
772 h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1);
773 hlist = hash_table[h];
774 n = stbiw__sbcount(hlist);
775 for (j=0; j < n; ++j) {
776 if (hlist[j]-data > i-32767) {
777 int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1);
778 if (e > best) { // if next match is better, bail on current match
779 bestloc = NULL;
780 break;
781 }
782 }
783 }
784 }
785
786 if (bestloc) {
787 int d = (int) (data+i - bestloc); // distance back
788 STBIW_ASSERT(d <= 32767 && best <= 258);
789 for (j=0; best > lengthc[j+1]-1; ++j);
790 stbiw__zlib_huff(j+257);
791 if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]);
792 for (j=0; d > distc[j+1]-1; ++j);
793 stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5);
794 if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]);
795 i += best;
796 } else {
797 stbiw__zlib_huffb(data[i]);
798 ++i;
799 }
800 }
801 // write out final bytes
802 for (;i < data_len; ++i)
803 stbiw__zlib_huffb(data[i]);
804 stbiw__zlib_huff(256); // end of block
805 // pad with 0 bits to byte boundary
806 while (bitcount)
807 stbiw__zlib_add(0,1);
808
809 for (i=0; i < stbiw__ZHASH; ++i)
810 (void) stbiw__sbfree(hash_table[i]);
811
812 {
813 // compute adler32 on input
814 unsigned int s1=1, s2=0;
815 int blocklen = (int) (data_len % 5552);
816 j=0;
817 while (j < data_len) {
818 for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1;
819 s1 %= 65521, s2 %= 65521;
820 j += blocklen;
821 blocklen = 5552;
822 }
823 stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8));
824 stbiw__sbpush(out, STBIW_UCHAR(s2));
825 stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8));
826 stbiw__sbpush(out, STBIW_UCHAR(s1));
827 }
828 *out_len = stbiw__sbn(out);
829 // make returned pointer freeable
830 STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len);
831 return (unsigned char *) stbiw__sbraw(out);
832 }
833
stbiw__crc32(unsigned char * buffer,int len)834 static unsigned int stbiw__crc32(unsigned char *buffer, int len)
835 {
836 static unsigned int crc_table[256] =
837 {
838 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
839 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
840 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
841 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
842 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
843 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
844 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
845 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
846 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
847 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
848 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
849 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
850 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
851 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
852 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
853 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
854 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
855 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
856 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
857 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
858 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
859 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
860 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
861 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
862 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
863 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
864 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
865 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
866 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
867 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
868 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
869 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
870 };
871
872 unsigned int crc = ~0u;
873 int i;
874 for (i=0; i < len; ++i)
875 crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
876 return ~crc;
877 }
878
879 #define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4)
880 #define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v));
881 #define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3])
882
stbiw__wpcrc(unsigned char ** data,int len)883 static void stbiw__wpcrc(unsigned char **data, int len)
884 {
885 unsigned int crc = stbiw__crc32(*data - len - 4, len+4);
886 stbiw__wp32(*data, crc);
887 }
888
stbiw__paeth(int a,int b,int c)889 static unsigned char stbiw__paeth(int a, int b, int c)
890 {
891 int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c);
892 if (pa <= pb && pa <= pc) return STBIW_UCHAR(a);
893 if (pb <= pc) return STBIW_UCHAR(b);
894 return STBIW_UCHAR(c);
895 }
896
stbi_write_png_to_mem(unsigned char * pixels,int stride_bytes,int x,int y,int n,int * out_len)897 unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)
898 {
899 int ctype[5] = { -1, 0, 4, 2, 6 };
900 unsigned char sig[8] = { 137,80,78,71,13,10,26,10 };
901 unsigned char *out,*o, *filt, *zlib;
902 signed char *line_buffer;
903 int i,j,k,p,zlen;
904
905 if (stride_bytes == 0)
906 stride_bytes = x * n;
907
908 filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0;
909 line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; }
910 for (j=0; j < y; ++j) {
911 static int mapping[] = { 0,1,2,3,4 };
912 static int firstmap[] = { 0,1,0,5,6 };
913 int *mymap = j ? mapping : firstmap;
914 int best = 0, bestval = 0x7fffffff;
915 for (p=0; p < 2; ++p) {
916 for (k= p?best:0; k < 5; ++k) {
917 int type = mymap[k],est=0;
918 unsigned char *z = pixels + stride_bytes*j;
919 for (i=0; i < n; ++i)
920 switch (type) {
921 case 0: line_buffer[i] = z[i]; break;
922 case 1: line_buffer[i] = z[i]; break;
923 case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break;
924 case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break;
925 case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break;
926 case 5: line_buffer[i] = z[i]; break;
927 case 6: line_buffer[i] = z[i]; break;
928 }
929 for (i=n; i < x*n; ++i) {
930 switch (type) {
931 case 0: line_buffer[i] = z[i]; break;
932 case 1: line_buffer[i] = z[i] - z[i-n]; break;
933 case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break;
934 case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break;
935 case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break;
936 case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break;
937 case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break;
938 }
939 }
940 if (p) break;
941 for (i=0; i < x*n; ++i)
942 est += abs((signed char) line_buffer[i]);
943 if (est < bestval) { bestval = est; best = k; }
944 }
945 }
946 // when we get here, best contains the filter type, and line_buffer contains the data
947 filt[j*(x*n+1)] = (unsigned char) best;
948 STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n);
949 }
950 STBIW_FREE(line_buffer);
951 zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory
952 STBIW_FREE(filt);
953 if (!zlib) return 0;
954
955 // each tag requires 12 bytes of overhead
956 out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12);
957 if (!out) return 0;
958 *out_len = 8 + 12+13 + 12+zlen + 12;
959
960 o=out;
961 STBIW_MEMMOVE(o,sig,8); o+= 8;
962 stbiw__wp32(o, 13); // header length
963 stbiw__wptag(o, "IHDR");
964 stbiw__wp32(o, x);
965 stbiw__wp32(o, y);
966 *o++ = 8;
967 *o++ = STBIW_UCHAR(ctype[n]);
968 *o++ = 0;
969 *o++ = 0;
970 *o++ = 0;
971 stbiw__wpcrc(&o,13);
972
973 stbiw__wp32(o, zlen);
974 stbiw__wptag(o, "IDAT");
975 STBIW_MEMMOVE(o, zlib, zlen);
976 o += zlen;
977 STBIW_FREE(zlib);
978 stbiw__wpcrc(&o, zlen);
979
980 stbiw__wp32(o,0);
981 stbiw__wptag(o, "IEND");
982 stbiw__wpcrc(&o,0);
983
984 STBIW_ASSERT(o == out + *out_len);
985
986 return out;
987 }
988
989 #ifndef STBI_WRITE_NO_STDIO
stbi_write_png(char const * filename,int x,int y,int comp,const void * data,int stride_bytes)990 STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
991 {
992 FILE *f;
993 int len;
994 unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);
995 if (png == NULL) return 0;
996 f = fopen(filename, "wb");
997 if (!f) { STBIW_FREE(png); return 0; }
998 fwrite(png, 1, len, f);
999 fclose(f);
1000 STBIW_FREE(png);
1001 return 1;
1002 }
1003 #endif
1004
stbi_write_png_to_func(stbi_write_func * func,void * context,int x,int y,int comp,const void * data,int stride_bytes)1005 STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes)
1006 {
1007 int len;
1008 unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);
1009 if (png == NULL) return 0;
1010 func(context, png, len);
1011 STBIW_FREE(png);
1012 return 1;
1013 }
1014
1015 #endif // STB_IMAGE_WRITE_IMPLEMENTATION
1016
1017 /* Revision history
1018 1.01 (2016-01-16)
1019 STBIW_REALLOC_SIZED: support allocators with no realloc support
1020 avoid race-condition in crc initialization
1021 minor compile issues
1022 1.00 (2015-09-14)
1023 installable file IO function
1024 0.99 (2015-09-13)
1025 warning fixes; TGA rle support
1026 0.98 (2015-04-08)
1027 added STBIW_MALLOC, STBIW_ASSERT etc
1028 0.97 (2015-01-18)
1029 fixed HDR asserts, rewrote HDR rle logic
1030 0.96 (2015-01-17)
1031 add HDR output
1032 fix monochrome BMP
1033 0.95 (2014-08-17)
1034 add monochrome TGA output
1035 0.94 (2014-05-31)
1036 rename private functions to avoid conflicts with stb_image.h
1037 0.93 (2014-05-27)
1038 warning fixes
1039 0.92 (2010-08-01)
1040 casts to unsigned char to fix warnings
1041 0.91 (2010-07-17)
1042 first public release
1043 0.90 first internal release
1044 */
1045