1 /* stbiw-0.92 - public domain - http://nothings.org/stb/stb_image_write.h
2 writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010
3 no warranty implied; use at your own risk
4
5
6 Before including,
7
8 #define STB_IMAGE_WRITE_IMPLEMENTATION
9
10 in the file that you want to have the implementation.
11
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 simplicitly, not optimal image file size
21 or run-time performance.
22
23 USAGE:
24
25 There are three functions, one for each image file format:
26
27 int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
28 int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
29 int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
30
31 Each function returns 0 on failure and non-0 on success.
32
33 The functions create an image file defined by the parameters. The image
34 is a rectangle of pixels stored from left-to-right, top-to-bottom.
35 Each pixel contains 'comp' channels of data stored interleaved with 8-bits
36 per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is
37 monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall.
38 The *data pointer points to the first byte of the top-left-most pixel.
39 For PNG, "stride_in_bytes" is the distance in bytes from the first byte of
40 a row of pixels to the first byte of the next row of pixels.
41
42 PNG creates output files with the same number of components as the input.
43 The BMP and TGA formats expand Y to RGB in the file format. BMP does not
44 output alpha.
45
46 PNG supports writing rectangles of data even when the bytes storing rows of
47 data are not consecutive in memory (e.g. sub-rectangles of a larger image),
48 by supplying the stride between the beginning of adjacent rows. The other
49 formats do not. (Thus you cannot write a native-format BMP through the BMP
50 writer, both because it is in BGR order and because it may have padding
51 at the end of the line.)
52 */
53
54 #ifndef INCLUDE_STB_IMAGE_WRITE_H
55 #define INCLUDE_STB_IMAGE_WRITE_H
56
57 #ifdef __cplusplus
58 extern "C"
59 {
60 #endif
61
62 extern int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
63 extern int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
64 extern int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
65
66 #ifdef __cplusplus
67 }
68 #endif
69
70 #endif //INCLUDE_STB_IMAGE_WRITE_H
71
72 #ifdef STB_IMAGE_WRITE_IMPLEMENTATION
73
74 #include <stdarg.h>
75 #include <stdlib.h>
76 #include <stdio.h>
77 #include <string.h>
78 #include <assert.h>
79
80 typedef unsigned int stbiw_uint32;
81 typedef int stb_image_write_test[sizeof(stbiw_uint32) == 4 ? 1 : -1];
82
writefv(FILE * f,const char * fmt,va_list v)83 static void writefv(FILE *f, const char *fmt, va_list v)
84 {
85 while (*fmt)
86 {
87 switch (*fmt++)
88 {
89 case ' ':
90 break;
91 case '1':
92 {
93 unsigned char x = (unsigned char)va_arg(v, int);
94 fputc(x, f);
95 break;
96 }
97 case '2':
98 {
99 int x = va_arg(v, int);
100 unsigned char b[2];
101 b[0] = (unsigned char)x;
102 b[1] = (unsigned char)(x >> 8);
103 fwrite(b, 2, 1, f);
104 break;
105 }
106 case '4':
107 {
108 stbiw_uint32 x = va_arg(v, int);
109 unsigned char b[4];
110 b[0] = (unsigned char)x;
111 b[1] = (unsigned char)(x >> 8);
112 b[2] = (unsigned char)(x >> 16);
113 b[3] = (unsigned char)(x >> 24);
114 fwrite(b, 4, 1, f);
115 break;
116 }
117 default:
118 assert(0);
119 return;
120 }
121 }
122 }
123
write3(FILE * f,unsigned char a,unsigned char b,unsigned char c)124 static void write3(FILE *f, unsigned char a, unsigned char b, unsigned char c)
125 {
126 unsigned char arr[3];
127 arr[0] = a, arr[1] = b, arr[2] = c;
128 fwrite(arr, 3, 1, f);
129 }
130
write_pixels(FILE * f,int rgb_dir,int vdir,int x,int y,int comp,void * data,int write_alpha,int scanline_pad)131 static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad)
132 {
133 unsigned char bg[3] = {255, 0, 255}, px[3];
134 stbiw_uint32 zero = 0;
135 int i, j, k, j_end;
136
137 if (y <= 0)
138 return;
139
140 if (vdir < 0)
141 j_end = -1, j = y - 1;
142 else
143 j_end = y, j = 0;
144
145 for (; j != j_end; j += vdir)
146 {
147 for (i = 0; i < x; ++i)
148 {
149 unsigned char *d = (unsigned char *)data + (j * x + i) * comp;
150 if (write_alpha < 0)
151 fwrite(&d[comp - 1], 1, 1, f);
152 switch (comp)
153 {
154 case 1:
155 case 2:
156 write3(f, d[0], d[0], d[0]);
157 break;
158 case 4:
159 if (!write_alpha)
160 {
161 // composite against pink background
162 for (k = 0; k < 3; ++k)
163 px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255;
164 write3(f, px[1 - rgb_dir], px[1], px[1 + rgb_dir]);
165 break;
166 }
167 /* FALLTHROUGH */
168 case 3:
169 write3(f, d[1 - rgb_dir], d[1], d[1 + rgb_dir]);
170 break;
171 }
172 if (write_alpha > 0)
173 fwrite(&d[comp - 1], 1, 1, f);
174 }
175 fwrite(&zero, scanline_pad, 1, f);
176 }
177 }
178
outfile(char const * filename,int rgb_dir,int vdir,int x,int y,int comp,void * data,int alpha,int pad,const char * fmt,...)179 static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, const char *fmt, ...)
180 {
181 FILE *f;
182 if (y < 0 || x < 0) return 0;
183 f = fopen(filename, "wb");
184 if (f)
185 {
186 va_list v;
187 va_start(v, fmt);
188 writefv(f, fmt, v);
189 va_end(v);
190 write_pixels(f, rgb_dir, vdir, x, y, comp, data, alpha, pad);
191 fclose(f);
192 }
193 return f != NULL;
194 }
195
stbi_write_bmp(char const * filename,int x,int y,int comp,const void * data)196 int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)
197 {
198 int pad = (-x * 3) & 3;
199 return outfile(filename, -1, -1, x, y, comp, (void *)data, 0, pad,
200 "11 4 22 4"
201 "4 44 22 444444",
202 'B', 'M', 14 + 40 + (x * 3 + pad) * y, 0, 0, 14 + 40, // file header
203 40, x, y, 1, 24, 0, 0, 0, 0, 0, 0); // bitmap header
204 }
205
stbi_write_tga(char const * filename,int x,int y,int comp,const void * data)206 int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)
207 {
208 int has_alpha = !(comp & 1);
209 return outfile(filename, -1, -1, x, y, comp, (void *)data, has_alpha, 0,
210 "111 221 2222 11", 0, 0, 2, 0, 0, 0, 0, 0, x, y, 24 + 8 * has_alpha, 8 * has_alpha);
211 }
212
213 // stretchy buffer; stbi__sbpush() == vector<>::push_back() -- stbi__sbcount() == vector<>::size()
214 #define stbi__sbraw(a) ((int *)(a)-2)
215 #define stbi__sbm(a) stbi__sbraw(a)[0]
216 #define stbi__sbn(a) stbi__sbraw(a)[1]
217
218 #define stbi__sbneedgrow(a, n) ((a) == 0 || stbi__sbn(a) + n >= stbi__sbm(a))
219 #define stbi__sbmaybegrow(a, n) (stbi__sbneedgrow(a, (n)) ? stbi__sbgrow(a, n) : 0)
220 #define stbi__sbgrow(a, n) stbi__sbgrowf((void **)&(a), (n), sizeof(*(a)))
221
222 #define stbi__sbpush(a, v) (stbi__sbmaybegrow(a, 1), (a)[stbi__sbn(a)++] = (v))
223 #define stbi__sbcount(a) ((a) ? stbi__sbn(a) : 0)
224 #define stbi__sbfree(a) ((a) ? free(stbi__sbraw(a)), 0 : 0)
225
stbi__sbgrowf(void ** arr,int increment,int itemsize)226 static void *stbi__sbgrowf(void **arr, int increment, int itemsize)
227 {
228 int m = *arr ? 2 * stbi__sbm(*arr) + increment : increment + 1;
229 void *p = realloc(*arr ? stbi__sbraw(*arr) : 0, itemsize * m + sizeof(int) * 2);
230 assert(p);
231 if (p)
232 {
233 if (!*arr) ((int *)p)[1] = 0;
234 *arr = (void *)((int *)p + 2);
235 stbi__sbm(*arr) = m;
236 }
237 return *arr;
238 }
239
stbi__zlib_flushf(unsigned char * data,unsigned int * bitbuffer,int * bitcount)240 static unsigned char *stbi__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)
241 {
242 while (*bitcount >= 8)
243 {
244 stbi__sbpush(data, (unsigned char)*bitbuffer);
245 *bitbuffer >>= 8;
246 *bitcount -= 8;
247 }
248 return data;
249 }
250
stbi__zlib_bitrev(int code,int codebits)251 static int stbi__zlib_bitrev(int code, int codebits)
252 {
253 int res = 0;
254 while (codebits--)
255 {
256 res = (res << 1) | (code & 1);
257 code >>= 1;
258 }
259 return res;
260 }
261
stbi__zlib_countm(unsigned char * a,unsigned char * b,int limit)262 static unsigned int stbi__zlib_countm(unsigned char *a, unsigned char *b, int limit)
263 {
264 int i;
265 for (i = 0; i < limit && i < 258; ++i)
266 if (a[i] != b[i]) break;
267 return i;
268 }
269
stbi__zhash(unsigned char * data)270 static unsigned int stbi__zhash(unsigned char *data)
271 {
272 stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16);
273 hash ^= hash << 3;
274 hash += hash >> 5;
275 hash ^= hash << 4;
276 hash += hash >> 17;
277 hash ^= hash << 25;
278 hash += hash >> 6;
279 return hash;
280 }
281
282 #define stbi__zlib_flush() (out = stbi__zlib_flushf(out, &bitbuf, &bitcount))
283 #define stbi__zlib_add(code, codebits) \
284 (bitbuf |= (code) << bitcount, bitcount += (codebits), stbi__zlib_flush())
285 #define stbi__zlib_huffa(b, c) stbi__zlib_add(stbi__zlib_bitrev(b, c), c)
286 // default huffman tables
287 #define stbi__zlib_huff1(n) stbi__zlib_huffa(0x30 + (n), 8)
288 #define stbi__zlib_huff2(n) stbi__zlib_huffa(0x190 + (n)-144, 9)
289 #define stbi__zlib_huff3(n) stbi__zlib_huffa(0 + (n)-256, 7)
290 #define stbi__zlib_huff4(n) stbi__zlib_huffa(0xc0 + (n)-280, 8)
291 #define stbi__zlib_huff(n) ((n) <= 143 ? stbi__zlib_huff1(n) : (n) <= 255 ? stbi__zlib_huff2(n) : (n) <= 279 ? stbi__zlib_huff3(n) : stbi__zlib_huff4(n))
292 #define stbi__zlib_huffb(n) ((n) <= 143 ? stbi__zlib_huff1(n) : stbi__zlib_huff2(n))
293
294 #define stbi__ZHASH 16384
295
stbi_zlib_compress(unsigned char * data,int data_len,int * out_len,int quality)296 unsigned char *stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)
297 {
298 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};
299 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};
300 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};
301 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};
302 unsigned int bitbuf = 0;
303 int i, j, bitcount = 0;
304 unsigned char *out = NULL;
305 unsigned char **hash_table[stbi__ZHASH]; // 64KB on the stack!
306 if (quality < 5) quality = 5;
307
308 stbi__sbpush(out, 0x78); // DEFLATE 32K window
309 stbi__sbpush(out, 0x5e); // FLEVEL = 1
310 stbi__zlib_add(1, 1); // BFINAL = 1
311 stbi__zlib_add(1, 2); // B3YPE = 1 -- fixed huffman
312
313 for (i = 0; i < stbi__ZHASH; ++i)
314 hash_table[i] = NULL;
315
316 i = 0;
317 while (i < data_len - 3)
318 {
319 // hash next 3 bytes of data to be compressed
320 int h = stbi__zhash(data + i) & (stbi__ZHASH - 1), best = 3;
321 unsigned char *bestloc = 0;
322 unsigned char **hlist = hash_table[h];
323 int n = stbi__sbcount(hlist);
324 for (j = 0; j < n; ++j)
325 {
326 if (hlist[j] - data > i - 32768)
327 { // if entry lies within window
328 int d = stbi__zlib_countm(hlist[j], data + i, data_len - i);
329 if (d >= best) best = d, bestloc = hlist[j];
330 }
331 }
332 // when hash table entry is too long, delete half the entries
333 if (hash_table[h] && stbi__sbn(hash_table[h]) == 2 * quality)
334 {
335 memcpy(hash_table[h], hash_table[h] + quality, sizeof(hash_table[h][0]) * quality);
336 stbi__sbn(hash_table[h]) = quality;
337 }
338 stbi__sbpush(hash_table[h], data + i);
339
340 if (bestloc)
341 {
342 // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal
343 h = stbi__zhash(data + i + 1) & (stbi__ZHASH - 1);
344 hlist = hash_table[h];
345 n = stbi__sbcount(hlist);
346 for (j = 0; j < n; ++j)
347 {
348 if (hlist[j] - data > i - 32767)
349 {
350 int e = stbi__zlib_countm(hlist[j], data + i + 1, data_len - i - 1);
351 if (e > best)
352 { // if next match is better, bail on current match
353 bestloc = NULL;
354 break;
355 }
356 }
357 }
358 }
359
360 if (bestloc)
361 {
362 int d = data + i - bestloc; // distance back
363 assert(d <= 32767 && best <= 258);
364 for (j = 0; best > lengthc[j + 1] - 1; ++j)
365 ;
366 stbi__zlib_huff(j + 257);
367 if (lengtheb[j]) stbi__zlib_add(best - lengthc[j], lengtheb[j]);
368 for (j = 0; d > distc[j + 1] - 1; ++j)
369 ;
370 stbi__zlib_add(stbi__zlib_bitrev(j, 5), 5);
371 if (disteb[j]) stbi__zlib_add(d - distc[j], disteb[j]);
372 i += best;
373 }
374 else
375 {
376 stbi__zlib_huffb(data[i]);
377 ++i;
378 }
379 }
380 // write out final bytes
381 for (; i < data_len; ++i)
382 stbi__zlib_huffb(data[i]);
383 stbi__zlib_huff(256); // end of block
384 // pad with 0 bits to byte boundary
385 while (bitcount)
386 stbi__zlib_add(0, 1);
387
388 for (i = 0; i < stbi__ZHASH; ++i)
389 (void)stbi__sbfree(hash_table[i]);
390
391 {
392 // compute adler32 on input
393 unsigned int i = 0, s1 = 1, s2 = 0, blocklen = data_len % 5552;
394 int j = 0;
395 while (j < data_len)
396 {
397 for (i = 0; i < blocklen; ++i) s1 += data[j + i], s2 += s1;
398 s1 %= 65521, s2 %= 65521;
399 j += blocklen;
400 blocklen = 5552;
401 }
402 stbi__sbpush(out, (unsigned char)(s2 >> 8));
403 stbi__sbpush(out, (unsigned char)s2);
404 stbi__sbpush(out, (unsigned char)(s1 >> 8));
405 stbi__sbpush(out, (unsigned char)s1);
406 }
407 *out_len = stbi__sbn(out);
408 // make returned pointer freeable
409 memmove(stbi__sbraw(out), out, *out_len);
410 return (unsigned char *)stbi__sbraw(out);
411 }
412
stbi__crc32(unsigned char * buffer,int len)413 unsigned int stbi__crc32(unsigned char *buffer, int len)
414 {
415 static unsigned int crc_table[256];
416 unsigned int crc = ~0u;
417 int i, j;
418 if (crc_table[1] == 0)
419 for (i = 0; i < 256; i++)
420 for (crc_table[i] = i, j = 0; j < 8; ++j)
421 crc_table[i] = (crc_table[i] >> 1) ^ (crc_table[i] & 1 ? 0xedb88320 : 0);
422 for (i = 0; i < len; ++i)
423 crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
424 return ~crc;
425 }
426
427 #define stbi__wpng4(o, a, b, c, d) ((o)[0] = (unsigned char)(a), (o)[1] = (unsigned char)(b), (o)[2] = (unsigned char)(c), (o)[3] = (unsigned char)(d), (o) += 4)
428 #define stbi__wp32(data, v) stbi__wpng4(data, (v) >> 24, (v) >> 16, (v) >> 8, (v));
429 #define stbi__wptag(data, s) stbi__wpng4(data, s[0], s[1], s[2], s[3])
430
stbi__wpcrc(unsigned char ** data,int len)431 static void stbi__wpcrc(unsigned char **data, int len)
432 {
433 unsigned int crc = stbi__crc32(*data - len - 4, len + 4);
434 stbi__wp32(*data, crc);
435 }
436
stbi__paeth(int a,int b,int c)437 static unsigned char stbi__paeth(int a, int b, int c)
438 {
439 int p = a + b - c, pa = abs(p - a), pb = abs(p - b), pc = abs(p - c);
440 if (pa <= pb && pa <= pc) return (unsigned char)a;
441 if (pb <= pc) return (unsigned char)b;
442 return (unsigned char)c;
443 }
444
stbi_write_png_to_mem(unsigned char * pixels,int stride_bytes,int x,int y,int n,int * out_len)445 unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)
446 {
447 int ctype[5] = {-1, 0, 4, 2, 6};
448 unsigned char sig[8] = {137, 80, 78, 71, 13, 10, 26, 10};
449 unsigned char *out, *o, *filt, *zlib;
450 signed char *line_buffer;
451 int i, j, k, p, zlen;
452
453 if (stride_bytes == 0)
454 stride_bytes = x * n;
455
456 filt = (unsigned char *)malloc((x * n + 1) * y);
457 if (!filt) return 0;
458 line_buffer = (signed char *)malloc(x * n);
459 if (!line_buffer)
460 {
461 free(filt);
462 return 0;
463 }
464 for (j = 0; j < y; ++j)
465 {
466 static int mapping[] = {0, 1, 2, 3, 4};
467 static int firstmap[] = {0, 1, 0, 5, 6};
468 int *mymap = j ? mapping : firstmap;
469 int best = 0, bestval = 0x7fffffff;
470 for (p = 0; p < 2; ++p)
471 {
472 for (k = p ? best : 0; k < 5; ++k)
473 {
474 int type = mymap[k], est = 0;
475 unsigned char *z = pixels + stride_bytes * j;
476 for (i = 0; i < n; ++i)
477 switch (type)
478 {
479 case 0:
480 line_buffer[i] = z[i];
481 break;
482 case 1:
483 line_buffer[i] = z[i];
484 break;
485 case 2:
486 line_buffer[i] = z[i] - z[i - stride_bytes];
487 break;
488 case 3:
489 line_buffer[i] = z[i] - (z[i - stride_bytes] >> 1);
490 break;
491 case 4:
492 line_buffer[i] = (signed char)(z[i] - stbi__paeth(0, z[i - stride_bytes], 0));
493 break;
494 case 5:
495 line_buffer[i] = z[i];
496 break;
497 case 6:
498 line_buffer[i] = z[i];
499 break;
500 }
501 for (i = n; i < x * n; ++i)
502 {
503 switch (type)
504 {
505 case 0:
506 line_buffer[i] = z[i];
507 break;
508 case 1:
509 line_buffer[i] = z[i] - z[i - n];
510 break;
511 case 2:
512 line_buffer[i] = z[i] - z[i - stride_bytes];
513 break;
514 case 3:
515 line_buffer[i] = z[i] - ((z[i - n] + z[i - stride_bytes]) >> 1);
516 break;
517 case 4:
518 line_buffer[i] = z[i] - stbi__paeth(z[i - n], z[i - stride_bytes], z[i - stride_bytes - n]);
519 break;
520 case 5:
521 line_buffer[i] = z[i] - (z[i - n] >> 1);
522 break;
523 case 6:
524 line_buffer[i] = z[i] - stbi__paeth(z[i - n], 0, 0);
525 break;
526 }
527 }
528 if (p) break;
529 for (i = 0; i < x * n; ++i)
530 est += abs((signed char)line_buffer[i]);
531 if (est < bestval)
532 {
533 bestval = est;
534 best = k;
535 }
536 }
537 }
538 // when we get here, best contains the filter type, and line_buffer contains the data
539 filt[j * (x * n + 1)] = (unsigned char)best;
540 memcpy(filt + j * (x * n + 1) + 1, line_buffer, x * n);
541 }
542 free(line_buffer);
543 zlib = stbi_zlib_compress(filt, y * (x * n + 1), &zlen, 8); // increase 8 to get smaller but use more memory
544 free(filt);
545 if (!zlib) return 0;
546
547 // each tag requires 12 bytes of overhead
548 out = (unsigned char *)malloc(8 + 12 + 13 + 12 + zlen + 12);
549 if (!out) return 0;
550 *out_len = 8 + 12 + 13 + 12 + zlen + 12;
551
552 o = out;
553 memcpy(o, sig, 8);
554 o += 8;
555 stbi__wp32(o, 13); // header length
556 stbi__wptag(o, "IHDR");
557 stbi__wp32(o, x);
558 stbi__wp32(o, y);
559 *o++ = 8;
560 *o++ = (unsigned char)ctype[n];
561 *o++ = 0;
562 *o++ = 0;
563 *o++ = 0;
564 stbi__wpcrc(&o, 13);
565
566 stbi__wp32(o, zlen);
567 stbi__wptag(o, "IDAT");
568 memcpy(o, zlib, zlen);
569 o += zlen;
570 free(zlib);
571 stbi__wpcrc(&o, zlen);
572
573 stbi__wp32(o, 0);
574 stbi__wptag(o, "IEND");
575 stbi__wpcrc(&o, 0);
576
577 assert(o == out + *out_len);
578
579 return out;
580 }
581
stbi_write_png(char const * filename,int x,int y,int comp,const void * data,int stride_bytes)582 int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
583 {
584 FILE *f;
585 int len;
586 unsigned char *png = stbi_write_png_to_mem((unsigned char *)data, stride_bytes, x, y, comp, &len);
587 if (!png) return 0;
588 f = fopen(filename, "wb");
589 if (!f)
590 {
591 free(png);
592 return 0;
593 }
594 fwrite(png, 1, len, f);
595 fclose(f);
596 free(png);
597 return 1;
598 }
599 #endif // STB_IMAGE_WRITE_IMPLEMENTATION
600
601 /* Revision history
602
603 0.92 (2010-08-01)
604 casts to unsigned char to fix warnings
605 0.91 (2010-07-17)
606 first public release
607 0.90 first internal release
608 */
609