stb_image_write.h 67.9 KB
Newer Older
S
Sean Barrett 已提交
1
/* stb_image_write - v1.15 - public domain - http://nothings.org/stb
2
   writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015
3
                                     no warranty implied; use at your own risk
S
Sean Barrett 已提交
4

5
   Before #including,
S
Sean Barrett 已提交
6

7
       #define STB_IMAGE_WRITE_IMPLEMENTATION
S
Sean Barrett 已提交
8

9
   in the file that you want to have the implementation.
S
Sean Barrett 已提交
10

11
   Will probably not work correctly with strict-aliasing optimizations.
S
Sean Barrett 已提交
12 13 14

ABOUT:

15
   This header file is a library for writing images to C stdio or a callback.
S
Sean Barrett 已提交
16 17

   The PNG output is not optimal; it is 20-50% larger than the file
18 19 20 21
   written by a decent optimizing implementation; though providing a custom
   zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that.
   This library is designed for source code compactness and simplicity,
   not optimal image file size or run-time performance.
S
Sean Barrett 已提交
22

23 24 25 26 27
BUILDING:

   You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h.
   You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace
   malloc,realloc,free.
28 29 30 31 32 33
   You can #define STBIW_MEMMOVE() to replace memmove()
   You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function
   for PNG compression (instead of the builtin one), it must have the following signature:
   unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality);
   The returned data will be freed with STBIW_FREE() (free() by default),
   so it must be heap allocated with STBIW_MALLOC() (malloc() by default),
34

35 36 37 38 39 40 41 42
UNICODE:

   If compiling for Windows and you wish to use Unicode filenames, compile
   with
       #define STBIW_WINDOWS_UTF8
   and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert
   Windows wchar_t filenames to utf8.

S
Sean Barrett 已提交
43 44
USAGE:

45
   There are five functions, one for each image file format:
S
Sean Barrett 已提交
46 47 48 49

     int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
     int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
     int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
S
Sean Barrett 已提交
50
     int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality);
J
Jonas Karlsson 已提交
51
     int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
S
Sean Barrett 已提交
52

53 54 55
     void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically

   There are also five equivalent functions that use an arbitrary write function. You are
56
   expected to open/close your file-equivalent before and after calling these:
57

58 59 60 61
     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);
     int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);
     int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);
     int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);
62
     int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality);
63

64 65 66
   where the callback is:
      void stbi_write_func(void *context, void *data, int size);

67 68 69 70 71 72
   You can configure it with these global variables:
      int stbi_write_tga_with_rle;             // defaults to true; set to 0 to disable RLE
      int stbi_write_png_compression_level;    // defaults to 8; set to higher for more compression
      int stbi_write_force_png_filter;         // defaults to -1; set to 0..5 to force a filter mode


73
   You can define STBI_WRITE_NO_STDIO to disable the file variant of these
74 75
   functions, so the library will not use stdio.h at all. However, this will
   also disable HDR writing, because it requires stdio for formatted output.
S
Sean Barrett 已提交
76 77

   Each function returns 0 on failure and non-0 on success.
78

S
Sean Barrett 已提交
79 80 81 82 83 84 85 86 87 88
   The functions create an image file defined by the parameters. The image
   is a rectangle of pixels stored from left-to-right, top-to-bottom.
   Each pixel contains 'comp' channels of data stored interleaved with 8-bits
   per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is
   monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall.
   The *data pointer points to the first byte of the top-left-most pixel.
   For PNG, "stride_in_bytes" is the distance in bytes from the first byte of
   a row of pixels to the first byte of the next row of pixels.

   PNG creates output files with the same number of components as the input.
89
   The BMP format expands Y to RGB in the file format and does not
S
Sean Barrett 已提交
90
   output alpha.
91

S
Sean Barrett 已提交
92 93 94 95 96 97
   PNG supports writing rectangles of data even when the bytes storing rows of
   data are not consecutive in memory (e.g. sub-rectangles of a larger image),
   by supplying the stride between the beginning of adjacent rows. The other
   formats do not. (Thus you cannot write a native-format BMP through the BMP
   writer, both because it is in BGR order and because it may have padding
   at the end of the line.)
B
baldurk 已提交
98

99
   PNG allows you to set the deflate compression level by setting the global
100
   variable 'stbi_write_png_compression_level' (it defaults to 8).
101

B
baldurk 已提交
102 103 104
   HDR expects linear float data. Since the format is always 32-bit rgb(e)
   data, alpha (if provided) is discarded, and for monochrome data it is
   replicated across all three channels.
105

S
Sean Barrett 已提交
106 107
   TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed
   data, set the global variable 'stbi_write_tga_with_rle' to 0.
S
Sean Barrett 已提交
108

109 110 111
   JPEG does ignore alpha channels in input data; quality is between 1 and 100.
   Higher quality looks better but results in a bigger image.
   JPEG baseline (no JPEG progressive).
S
Sean Barrett 已提交
112

113 114
CREDITS:

115

S
Sean Barrett 已提交
116
   Sean Barrett           -    PNG/BMP/TGA
117 118 119 120 121 122 123
   Baldur Karlsson        -    HDR
   Jean-Sebastien Guay    -    TGA monochrome
   Tim Kelsey             -    misc enhancements
   Alan Hickman           -    TGA RLE
   Emmanuel Julien        -    initial file IO callback implementation
   Jon Olick              -    original jo_jpeg.cpp code
   Daniel Gibson          -    integrate JPEG, allow external zlib
124
   Aarni Koskela          -    allow choosing PNG filter
125

126 127
   bugfixes:
      github:Chribba
S
credits  
Sean Barrett 已提交
128
      Guillaume Chereau
S
Sean Barrett 已提交
129
      github:jry2
130
      github:romigrou
131
      Sergio Gonzalez
S
Sean Barrett 已提交
132
      Jonas Karlsson
S
Sean Barrett 已提交
133
      Filip Wasil
134
      Thatcher Ulrich
135
      github:poppolopoppo
136
      Patrick Boettcher
137
      github:xeekworx
138
      Cap Petschulat
S
Sean Barrett 已提交
139 140
      Simon Rodriguez
      Ivan Tikhonov
S
Sean Barrett 已提交
141 142
      github:ignotion
      Adam Schackart
143

144 145
LICENSE

146
  See end of file for license information.
147

S
Sean Barrett 已提交
148 149 150 151 152
*/

#ifndef INCLUDE_STB_IMAGE_WRITE_H
#define INCLUDE_STB_IMAGE_WRITE_H

153 154
#include <stdlib.h>

155
// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline'
S
Sean Barrett 已提交
156
#ifndef STBIWDEF
157
#ifdef STB_IMAGE_WRITE_STATIC
158 159 160 161
#define STBIWDEF  static
#else
#ifdef __cplusplus
#define STBIWDEF  extern "C"
162
#else
163 164
#define STBIWDEF  extern
#endif
165
#endif
S
Sean Barrett 已提交
166
#endif
167

168 169 170 171
#ifndef STB_IMAGE_WRITE_STATIC  // C++ forbids static forward declarations
extern int stbi_write_tga_with_rle;
extern int stbi_write_png_compression_level;
extern int stbi_write_force_png_filter;
S
Sean Barrett 已提交
172
#endif
173

174
#ifndef STBI_WRITE_NO_STDIO
175 176 177 178
STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void  *data, int stride_in_bytes);
STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void  *data);
STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void  *data);
STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
179
STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void  *data, int quality);
180 181 182 183

#ifdef STBI_WINDOWS_UTF8
STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input);
#endif
184
#endif
185

186
typedef void stbi_write_func(void *context, void *data, int size);
187

188 189 190 191
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);
STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);
STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);
STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);
192
STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void  *data, int quality);
S
Sean Barrett 已提交
193

194
STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean);
195

S
Sean Barrett 已提交
196 197 198 199
#endif//INCLUDE_STB_IMAGE_WRITE_H

#ifdef STB_IMAGE_WRITE_IMPLEMENTATION

S
Sean Barrett 已提交
200
#ifdef _WIN32
201
   #ifndef _CRT_SECURE_NO_WARNINGS
S
Sean Barrett 已提交
202
   #define _CRT_SECURE_NO_WARNINGS
203 204
   #endif
   #ifndef _CRT_NONSTDC_NO_DEPRECATE
S
Sean Barrett 已提交
205
   #define _CRT_NONSTDC_NO_DEPRECATE
206
   #endif
S
Sean Barrett 已提交
207 208
#endif

209 210 211
#ifndef STBI_WRITE_NO_STDIO
#include <stdio.h>
#endif // STBI_WRITE_NO_STDIO
212

S
Sean Barrett 已提交
213 214 215
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
216
#include <math.h>
S
Sean Barrett 已提交
217

218
#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED))
219
// ok
220
#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED)
221 222
// ok
#else
223
#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)."
224 225
#endif

226
#ifndef STBIW_MALLOC
227 228 229
#define STBIW_MALLOC(sz)        malloc(sz)
#define STBIW_REALLOC(p,newsz)  realloc(p,newsz)
#define STBIW_FREE(p)           free(p)
230
#endif
231 232 233 234 235 236

#ifndef STBIW_REALLOC_SIZED
#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz)
#endif


237 238 239 240
#ifndef STBIW_MEMMOVE
#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz)
#endif

241

242
#ifndef STBIW_ASSERT
243
#include <assert.h>
244
#define STBIW_ASSERT(x) assert(x)
245 246
#endif

247 248
#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff)

249
#ifdef STB_IMAGE_WRITE_STATIC
250
static int stbi_write_png_compression_level = 8;
251 252 253 254 255 256 257
static int stbi_write_tga_with_rle = 1;
static int stbi_write_force_png_filter = -1;
#else
int stbi_write_png_compression_level = 8;
int stbi_write_tga_with_rle = 1;
int stbi_write_force_png_filter = -1;
#endif
258

259 260
static int stbi__flip_vertically_on_write = 0;

261
STBIWDEF void stbi_flip_vertically_on_write(int flag)
262 263 264 265
{
   stbi__flip_vertically_on_write = flag;
}

266 267
typedef struct
{
268 269
   stbi_write_func *func;
   void *context;
270 271
   unsigned char buffer[64];
   int buf_used;
272 273 274
} stbi__write_context;

// initialize a callback-based context
275
static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context)
276
{
277 278
   s->func    = c;
   s->context = context;
279 280 281 282
}

#ifndef STBI_WRITE_NO_STDIO

283
static void stbi__stdio_write(void *context, void *data, int size)
284
{
285
   fwrite(data,1,size,(FILE*) context);
286 287
}

288 289 290 291 292 293 294 295 296 297 298
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
#ifdef __cplusplus
#define STBIW_EXTERN extern "C"
#else
#define STBIW_EXTERN extern
#endif
STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide);
STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);

STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
{
S
Sean Barrett 已提交
299
	return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
300 301 302 303
}
#endif

static FILE *stbiw__fopen(char const *filename, char const *mode)
304
{
305
   FILE *f;
306 307 308 309 310
#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
   wchar_t wMode[64];
   wchar_t wFilename[1024];
	if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)))
      return 0;
S
Sean Barrett 已提交
311

312 313 314 315 316 317
	if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)))
      return 0;

#if _MSC_VER >= 1400
	if (0 != _wfopen_s(&f, wFilename, wMode))
		f = 0;
318
#else
319
   f = _wfopen(wFilename, wMode);
320
#endif
321 322 323 324

#elif defined(_MSC_VER) && _MSC_VER >= 1400
   if (0 != fopen_s(&f, filename, mode))
      f=0;
325
#else
326
   f = fopen(filename, mode);
327
#endif
328 329 330 331 332 333
   return f;
}

static int stbi__start_write_file(stbi__write_context *s, const char *filename)
{
   FILE *f = stbiw__fopen(filename, "wb");
334
   stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f);
S
Sean Barrett 已提交
335
   return f != NULL;
336 337 338 339
}

static void stbi__end_write_file(stbi__write_context *s)
{
340
   fclose((FILE *)s->context);
341 342 343 344
}

#endif // !STBI_WRITE_NO_STDIO

S
Sean Barrett 已提交
345 346 347
typedef unsigned int stbiw_uint32;
typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];

S
Sean Barrett 已提交
348
static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v)
S
Sean Barrett 已提交
349 350 351 352
{
   while (*fmt) {
      switch (*fmt++) {
         case ' ': break;
353
         case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int));
354 355 356 357
                     s->func(s->context,&x,1);
                     break; }
         case '2': { int x = va_arg(v,int);
                     unsigned char b[2];
358 359
                     b[0] = STBIW_UCHAR(x);
                     b[1] = STBIW_UCHAR(x>>8);
360 361 362 363
                     s->func(s->context,b,2);
                     break; }
         case '4': { stbiw_uint32 x = va_arg(v,int);
                     unsigned char b[4];
364 365 366 367
                     b[0]=STBIW_UCHAR(x);
                     b[1]=STBIW_UCHAR(x>>8);
                     b[2]=STBIW_UCHAR(x>>16);
                     b[3]=STBIW_UCHAR(x>>24);
368 369
                     s->func(s->context,b,4);
                     break; }
S
Sean Barrett 已提交
370
         default:
371
            STBIW_ASSERT(0);
S
Sean Barrett 已提交
372 373 374 375 376
            return;
      }
   }
}

S
Sean Barrett 已提交
377
static void stbiw__writef(stbi__write_context *s, const char *fmt, ...)
F
fahickman 已提交
378 379 380
{
   va_list v;
   va_start(v, fmt);
S
Sean Barrett 已提交
381
   stbiw__writefv(s, fmt, v);
F
fahickman 已提交
382 383 384
   va_end(v);
}

385 386 387 388 389 390 391 392
static void stbiw__write_flush(stbi__write_context *s)
{
   if (s->buf_used) {
      s->func(s->context, &s->buffer, s->buf_used);
      s->buf_used = 0;
   }
}

393 394 395 396 397
static void stbiw__putc(stbi__write_context *s, unsigned char c)
{
   s->func(s->context, &c, 1);
}

398 399 400 401 402 403 404
static void stbiw__write1(stbi__write_context *s, unsigned char a)
{
   if (s->buf_used + 1 > sizeof(s->buffer))
      stbiw__write_flush(s);
   s->buffer[s->buf_used++] = a;
}

S
Sean Barrett 已提交
405
static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c)
S
Sean Barrett 已提交
406
{
407 408 409 410 411 412 413 414
   int n;
   if (s->buf_used + 3 > sizeof(s->buffer))
      stbiw__write_flush(s);
   n = s->buf_used;
   s->buf_used = n+3;
   s->buffer[n+0] = a;
   s->buffer[n+1] = b;
   s->buffer[n+2] = c;
S
Sean Barrett 已提交
415 416
}

S
Sean Barrett 已提交
417
static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d)
S
Sean Barrett 已提交
418 419
{
   unsigned char bg[3] = { 255, 0, 255}, px[3];
F
fahickman 已提交
420 421 422
   int k;

   if (write_alpha < 0)
423
      stbiw__write1(s, d[comp - 1]);
424

F
fahickman 已提交
425
   switch (comp) {
426 427
      case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case
      case 1:
428
         if (expand_mono)
S
Sean Barrett 已提交
429
            stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp
430
         else
431
            stbiw__write1(s, d[0]);  // monochrome TGA
432 433 434 435 436 437
         break;
      case 4:
         if (!write_alpha) {
            // composite against pink background
            for (k = 0; k < 3; ++k)
               px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255;
S
Sean Barrett 已提交
438
            stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]);
439 440 441 442
            break;
         }
         /* FALLTHROUGH */
      case 3:
S
Sean Barrett 已提交
443
         stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]);
F
fahickman 已提交
444 445 446
         break;
   }
   if (write_alpha > 0)
447
      stbiw__write1(s, d[comp - 1]);
F
fahickman 已提交
448 449
}

S
Sean Barrett 已提交
450
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)
F
fahickman 已提交
451
{
S
Sean Barrett 已提交
452
   stbiw_uint32 zero = 0;
F
fahickman 已提交
453
   int i,j, j_end;
S
Sean Barrett 已提交
454 455 456 457

   if (y <= 0)
      return;

458 459 460
   if (stbi__flip_vertically_on_write)
      vdir *= -1;

461 462 463 464 465
   if (vdir < 0) {
      j_end = -1; j = y-1;
   } else {
      j_end =  y; j = 0;
   }
S
Sean Barrett 已提交
466 467 468 469

   for (; j != j_end; j += vdir) {
      for (i=0; i < x; ++i) {
         unsigned char *d = (unsigned char *) data + (j*x+i)*comp;
S
Sean Barrett 已提交
470
         stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d);
S
Sean Barrett 已提交
471
      }
472
      stbiw__write_flush(s);
473
      s->func(s->context, &zero, scanline_pad);
S
Sean Barrett 已提交
474 475 476
   }
}

S
Sean Barrett 已提交
477
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, ...)
S
Sean Barrett 已提交
478
{
479 480 481
   if (y < 0 || x < 0) {
      return 0;
   } else {
S
Sean Barrett 已提交
482 483
      va_list v;
      va_start(v, fmt);
S
Sean Barrett 已提交
484
      stbiw__writefv(s, fmt, v);
S
Sean Barrett 已提交
485
      va_end(v);
S
Sean Barrett 已提交
486
      stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono);
487
      return 1;
S
Sean Barrett 已提交
488 489 490
   }
}

S
Sean Barrett 已提交
491
static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data)
S
Sean Barrett 已提交
492 493
{
   int pad = (-x*3) & 3;
S
Sean Barrett 已提交
494
   return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad,
S
Sean Barrett 已提交
495 496 497 498 499
           "11 4 22 4" "4 44 22 444444",
           'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40,  // file header
            40, x,y, 1,24, 0,0,0,0,0,0);             // bitmap header
}

500
STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)
501
{
502
   stbi__write_context s = { 0 };
503 504
   stbi__start_write_callbacks(&s, func, context);
   return stbi_write_bmp_core(&s, x, y, comp, data);
505 506 507
}

#ifndef STBI_WRITE_NO_STDIO
508
STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)
509
{
510
   stbi__write_context s = { 0 };
S
Sean Barrett 已提交
511 512 513 514 515 516
   if (stbi__start_write_file(&s,filename)) {
      int r = stbi_write_bmp_core(&s, x, y, comp, data);
      stbi__end_write_file(&s);
      return r;
   } else
      return 0;
517 518 519
}
#endif //!STBI_WRITE_NO_STDIO

S
Sean Barrett 已提交
520
static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data)
S
Sean Barrett 已提交
521
{
S
Sean Barrett 已提交
522 523
   int has_alpha = (comp == 2 || comp == 4);
   int colorbytes = has_alpha ? comp-1 : comp;
524
   int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3
525 526 527

   if (y < 0 || x < 0)
      return 0;
F
fahickman 已提交
528

S
Sean Barrett 已提交
529
   if (!stbi_write_tga_with_rle) {
S
Sean Barrett 已提交
530
      return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0,
F
fahickman 已提交
531
         "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8);
532
   } else {
F
fahickman 已提交
533
      int i,j,k;
534
      int jend, jdir;
F
fahickman 已提交
535

S
Sean Barrett 已提交
536
      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);
F
fahickman 已提交
537

538 539 540 541 542 543 544 545 546 547 548
      if (stbi__flip_vertically_on_write) {
         j = 0;
         jend = y;
         jdir = 1;
      } else {
         j = y-1;
         jend = -1;
         jdir = -1;
      }
      for (; j != jend; j += jdir) {
         unsigned char *row = (unsigned char *) data + j * x * comp;
F
fahickman 已提交
549
         int len;
F
fahickman 已提交
550

F
fahickman 已提交
551
         for (i = 0; i < x; i += len) {
552
            unsigned char *begin = row + i * comp;
F
fahickman 已提交
553
            int diff = 1;
F
fahickman 已提交
554
            len = 1;
F
fahickman 已提交
555 556

            if (i < x - 1) {
F
fahickman 已提交
557 558
               ++len;
               diff = memcmp(begin, row + (i + 1) * comp, comp);
F
fahickman 已提交
559
               if (diff) {
F
fahickman 已提交
560 561 562 563 564
                  const unsigned char *prev = begin;
                  for (k = i + 2; k < x && len < 128; ++k) {
                     if (memcmp(prev, row + k * comp, comp)) {
                        prev += comp;
                        ++len;
F
fahickman 已提交
565
                     } else {
F
fahickman 已提交
566
                        --len;
F
fahickman 已提交
567 568 569 570
                        break;
                     }
                  }
               } else {
F
fahickman 已提交
571 572 573
                  for (k = i + 2; k < x && len < 128; ++k) {
                     if (!memcmp(begin, row + k * comp, comp)) {
                        ++len;
F
fahickman 已提交
574 575 576 577 578 579 580 581
                     } else {
                        break;
                     }
                  }
               }
            }

            if (diff) {
582
               unsigned char header = STBIW_UCHAR(len - 1);
583
               stbiw__write1(s, header);
F
fahickman 已提交
584
               for (k = 0; k < len; ++k) {
S
Sean Barrett 已提交
585
                  stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp);
F
fahickman 已提交
586 587
               }
            } else {
588
               unsigned char header = STBIW_UCHAR(len - 129);
589
               stbiw__write1(s, header);
S
Sean Barrett 已提交
590
               stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin);
F
fahickman 已提交
591 592 593
            }
         }
      }
594
      stbiw__write_flush(s);
F
fahickman 已提交
595
   }
596
   return 1;
S
Sean Barrett 已提交
597 598
}

599
STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)
600
{
601
   stbi__write_context s = { 0 };
602 603
   stbi__start_write_callbacks(&s, func, context);
   return stbi_write_tga_core(&s, x, y, comp, (void *) data);
604
}
F
fahickman 已提交
605

606
#ifndef STBI_WRITE_NO_STDIO
607
STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)
608
{
609
   stbi__write_context s = { 0 };
S
Sean Barrett 已提交
610 611 612 613 614 615
   if (stbi__start_write_file(&s,filename)) {
      int r = stbi_write_tga_core(&s, x, y, comp, (void *) data);
      stbi__end_write_file(&s);
      return r;
   } else
      return 0;
S
Sean Barrett 已提交
616
}
617
#endif
S
Sean Barrett 已提交
618

B
baldurk 已提交
619 620
// *************************************************************************************************
// Radiance RGBE HDR writer
621
// by Baldur Karlsson
622

B
baldurk 已提交
623 624
#define stbiw__max(a, b)  ((a) > (b) ? (a) : (b))

625
static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)
B
baldurk 已提交
626 627 628 629
{
   int exponent;
   float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2]));

F
Filip Wasil 已提交
630
   if (maxcomp < 1e-32f) {
B
baldurk 已提交
631 632
      rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
   } else {
633
      float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp;
B
baldurk 已提交
634

635 636 637
      rgbe[0] = (unsigned char)(linear[0] * normalize);
      rgbe[1] = (unsigned char)(linear[1] * normalize);
      rgbe[2] = (unsigned char)(linear[2] * normalize);
B
baldurk 已提交
638 639 640 641
      rgbe[3] = (unsigned char)(exponent + 128);
   }
}

642
static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte)
B
baldurk 已提交
643
{
644
   unsigned char lengthbyte = STBIW_UCHAR(length+128);
645
   STBIW_ASSERT(length+128 <= 255);
646 647
   s->func(s->context, &lengthbyte, 1);
   s->func(s->context, &databyte, 1);
B
baldurk 已提交
648 649
}

650
static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data)
B
baldurk 已提交
651
{
652
   unsigned char lengthbyte = STBIW_UCHAR(length);
653
   STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code
654 655
   s->func(s->context, &lengthbyte, 1);
   s->func(s->context, data, length);
B
baldurk 已提交
656 657
}

658
static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline)
B
baldurk 已提交
659 660 661 662 663 664 665 666 667 668 669 670
{
   unsigned char scanlineheader[4] = { 2, 2, 0, 0 };
   unsigned char rgbe[4];
   float linear[3];
   int x;

   scanlineheader[2] = (width&0xff00)>>8;
   scanlineheader[3] = (width&0x00ff);

   /* skip RLE for images too small or large */
   if (width < 8 || width >= 32768) {
      for (x=0; x < width; x++) {
S
Sean Barrett 已提交
671
         switch (ncomp) {
B
baldurk 已提交
672
            case 4: /* fallthrough */
S
Sean Barrett 已提交
673 674 675
            case 3: linear[2] = scanline[x*ncomp + 2];
                    linear[1] = scanline[x*ncomp + 1];
                    linear[0] = scanline[x*ncomp + 0];
B
baldurk 已提交
676
                    break;
677
            default:
S
Sean Barrett 已提交
678
                    linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
B
baldurk 已提交
679 680 681
                    break;
         }
         stbiw__linear_to_rgbe(rgbe, linear);
682
         s->func(s->context, rgbe, 4);
B
baldurk 已提交
683 684
      }
   } else {
S
Sean Barrett 已提交
685
      int c,r;
B
baldurk 已提交
686 687
      /* encode into scratch buffer */
      for (x=0; x < width; x++) {
S
Sean Barrett 已提交
688
         switch(ncomp) {
B
baldurk 已提交
689
            case 4: /* fallthrough */
S
Sean Barrett 已提交
690 691 692
            case 3: linear[2] = scanline[x*ncomp + 2];
                    linear[1] = scanline[x*ncomp + 1];
                    linear[0] = scanline[x*ncomp + 0];
B
baldurk 已提交
693
                    break;
694
            default:
S
Sean Barrett 已提交
695
                    linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];
B
baldurk 已提交
696 697 698 699 700 701 702 703 704
                    break;
         }
         stbiw__linear_to_rgbe(rgbe, linear);
         scratch[x + width*0] = rgbe[0];
         scratch[x + width*1] = rgbe[1];
         scratch[x + width*2] = rgbe[2];
         scratch[x + width*3] = rgbe[3];
      }

705
      s->func(s->context, scanlineheader, 4);
B
baldurk 已提交
706 707

      /* RLE each component separately */
S
Sean Barrett 已提交
708 709
      for (c=0; c < 4; c++) {
         unsigned char *comp = &scratch[width*c];
B
baldurk 已提交
710

S
Sean Barrett 已提交
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
         x = 0;
         while (x < width) {
            // find first run
            r = x;
            while (r+2 < width) {
               if (comp[r] == comp[r+1] && comp[r] == comp[r+2])
                  break;
               ++r;
            }
            if (r+2 >= width)
               r = width;
            // dump up to first run
            while (x < r) {
               int len = r-x;
               if (len > 128) len = 128;
726
               stbiw__write_dump_data(s, len, &comp[x]);
S
Sean Barrett 已提交
727 728 729 730 731 732
               x += len;
            }
            // if there's a run, output it
            if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd
               // find next byte after run
               while (r < width && comp[r] == comp[x])
733
                  ++r;
S
Sean Barrett 已提交
734 735 736 737
               // output run up to r
               while (x < r) {
                  int len = r-x;
                  if (len > 127) len = 127;
738
                  stbiw__write_run_data(s, len, comp[x]);
S
Sean Barrett 已提交
739
                  x += len;
B
baldurk 已提交
740 741 742 743 744 745 746
               }
            }
         }
      }
   }
}

S
Sean Barrett 已提交
747
static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data)
B
baldurk 已提交
748
{
749 750 751 752
   if (y <= 0 || x <= 0 || data == NULL)
      return 0;
   else {
      // Each component is stored separately. Allocate scratch space for full output scanline.
753
      unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4);
754 755 756 757 758
      int i, len;
      char buffer[128];
      char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n";
      s->func(s->context, header, sizeof(header)-1);

S
Sean Barrett 已提交
759 760
#ifdef __STDC_WANT_SECURE_LIB__
      len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE=          1.0000000000000\n\n-Y %d +X %d\n", y, x);
761
#else
762
      len = sprintf(buffer, "EXPOSURE=          1.0000000000000\n\n-Y %d +X %d\n", y, x);
763
#endif
764 765
      s->func(s->context, buffer, len);

766
      for(i=0; i < y; i++)
767
         stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i));
768
      STBIW_FREE(scratch);
769
      return 1;
B
baldurk 已提交
770 771 772
   }
}

773
STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data)
774
{
775
   stbi__write_context s = { 0 };
776 777 778 779
   stbi__start_write_callbacks(&s, func, context);
   return stbi_write_hdr_core(&s, x, y, comp, (float *) data);
}

780
#ifndef STBI_WRITE_NO_STDIO
781
STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data)
782
{
783
   stbi__write_context s = { 0 };
S
Sean Barrett 已提交
784 785 786 787 788 789
   if (stbi__start_write_file(&s,filename)) {
      int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data);
      stbi__end_write_file(&s);
      return r;
   } else
      return 0;
790 791 792 793 794 795 796 797
}
#endif // STBI_WRITE_NO_STDIO


//////////////////////////////////////////////////////////////////////////////
//
// PNG writer
//
798

799
#ifndef STBIW_ZLIB_COMPRESS
800
// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size()
801
#define stbiw__sbraw(a) ((int *) (void *) (a) - 2)
802 803
#define stbiw__sbm(a)   stbiw__sbraw(a)[0]
#define stbiw__sbn(a)   stbiw__sbraw(a)[1]
S
Sean Barrett 已提交
804

805 806 807
#define stbiw__sbneedgrow(a,n)  ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a))
#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0)
#define stbiw__sbgrow(a,n)  stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a)))
S
Sean Barrett 已提交
808

809 810
#define stbiw__sbpush(a, v)      (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v))
#define stbiw__sbcount(a)        ((a) ? stbiw__sbn(a) : 0)
811
#define stbiw__sbfree(a)         ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0)
S
Sean Barrett 已提交
812

813
static void *stbiw__sbgrowf(void **arr, int increment, int itemsize)
S
Sean Barrett 已提交
814
{
815
   int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1;
816
   void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2);
817
   STBIW_ASSERT(p);
S
Sean Barrett 已提交
818 819 820
   if (p) {
      if (!*arr) ((int *) p)[1] = 0;
      *arr = (void *) ((int *) p + 2);
821
      stbiw__sbm(*arr) = m;
S
Sean Barrett 已提交
822 823 824 825
   }
   return *arr;
}

826
static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)
S
Sean Barrett 已提交
827 828
{
   while (*bitcount >= 8) {
829
      stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer));
S
Sean Barrett 已提交
830 831 832 833 834 835
      *bitbuffer >>= 8;
      *bitcount -= 8;
   }
   return data;
}

836
static int stbiw__zlib_bitrev(int code, int codebits)
S
Sean Barrett 已提交
837 838 839 840 841 842 843 844 845
{
   int res=0;
   while (codebits--) {
      res = (res << 1) | (code & 1);
      code >>= 1;
   }
   return res;
}

846
static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit)
S
Sean Barrett 已提交
847 848 849 850 851 852 853
{
   int i;
   for (i=0; i < limit && i < 258; ++i)
      if (a[i] != b[i]) break;
   return i;
}

854
static unsigned int stbiw__zhash(unsigned char *data)
S
Sean Barrett 已提交
855 856 857 858 859 860 861 862 863 864 865
{
   stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16);
   hash ^= hash << 3;
   hash += hash >> 5;
   hash ^= hash << 4;
   hash += hash >> 17;
   hash ^= hash << 25;
   hash += hash >> 6;
   return hash;
}

866 867 868 869
#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount))
#define stbiw__zlib_add(code,codebits) \
      (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush())
#define stbiw__zlib_huffa(b,c)  stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c)
S
Sean Barrett 已提交
870
// default huffman tables
871 872 873 874 875 876
#define stbiw__zlib_huff1(n)  stbiw__zlib_huffa(0x30 + (n), 8)
#define stbiw__zlib_huff2(n)  stbiw__zlib_huffa(0x190 + (n)-144, 9)
#define stbiw__zlib_huff3(n)  stbiw__zlib_huffa(0 + (n)-256,7)
#define stbiw__zlib_huff4(n)  stbiw__zlib_huffa(0xc0 + (n)-280,8)
#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))
#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n))
S
Sean Barrett 已提交
877

878
#define stbiw__ZHASH   16384
S
Sean Barrett 已提交
879

880 881
#endif // STBIW_ZLIB_COMPRESS

882
STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)
S
Sean Barrett 已提交
883
{
884 885 886 887
#ifdef STBIW_ZLIB_COMPRESS
   // user provided a zlib compress implementation, use that
   return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality);
#else // use builtin
S
Sean Barrett 已提交
888 889 890 891 892 893 894
   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 };
   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,