|
|
|
@ -57,6 +57,12 @@ USAGE: |
|
|
|
|
where the callback is: |
|
|
|
|
void stbi_write_func(void *context, void *data, int size); |
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
You can define STBI_WRITE_NO_STDIO to disable the file variant of these |
|
|
|
|
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. |
|
|
|
@ -108,6 +114,7 @@ CREDITS: |
|
|
|
|
Emmanuel Julien - initial file IO callback implementation |
|
|
|
|
Jon Olick - original jo_jpeg.cpp code |
|
|
|
|
Daniel Gibson - integrate JPEG, allow external zlib |
|
|
|
|
Aarni Koskela - allow choosing PNG filter |
|
|
|
|
|
|
|
|
|
bugfixes: |
|
|
|
|
github:Chribba |
|
|
|
@ -138,10 +145,12 @@ extern "C" { |
|
|
|
|
#define STBIWDEF static |
|
|
|
|
#else |
|
|
|
|
#define STBIWDEF extern |
|
|
|
|
extern int stbi_write_tga_with_rle; |
|
|
|
|
extern int stbi_write_png_level; |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
STBIWDEF int stbi_write_tga_with_rle; |
|
|
|
|
STBIWDEF int stbi_write_png_comperssion_level; |
|
|
|
|
STBIWDEF int stbi_write_force_png_filter; |
|
|
|
|
|
|
|
|
|
#ifndef STBI_WRITE_NO_STDIO |
|
|
|
|
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); |
|
|
|
@ -158,7 +167,7 @@ STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, |
|
|
|
|
STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); |
|
|
|
|
STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); |
|
|
|
|
|
|
|
|
|
extern void stbi_flip_vertically_on_write(int flip_boolean); |
|
|
|
|
STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); |
|
|
|
|
|
|
|
|
|
#ifdef __cplusplus |
|
|
|
|
} |
|
|
|
@ -217,9 +226,19 @@ extern void stbi_flip_vertically_on_write(int flip_boolean); |
|
|
|
|
|
|
|
|
|
#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) |
|
|
|
|
|
|
|
|
|
#ifdef STB_IMAGE_WRITE_STATIC |
|
|
|
|
static stbi__flip_vertically_on_write=0; |
|
|
|
|
static int stbi_write_png_compression level = 8; |
|
|
|
|
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__flip_vertically_on_write=0; |
|
|
|
|
int stbi_write_tga_with_rle = 1; |
|
|
|
|
int stbi_write_force_png_filter = -1; |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
void stbi_flip_vertically_on_write(int flag) |
|
|
|
|
STBIWDEF void stbi_flip_vertically_on_write(int flag) |
|
|
|
|
{ |
|
|
|
|
stbi__flip_vertically_on_write = flag; |
|
|
|
|
} |
|
|
|
@ -261,12 +280,6 @@ static void stbi__end_write_file(stbi__write_context *s) |
|
|
|
|
typedef unsigned int stbiw_uint32; |
|
|
|
|
typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; |
|
|
|
|
|
|
|
|
|
#ifdef STB_IMAGE_WRITE_STATIC |
|
|
|
|
static int stbi_write_tga_with_rle = 1; |
|
|
|
|
#else |
|
|
|
|
int stbi_write_tga_with_rle = 1; |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) |
|
|
|
|
{ |
|
|
|
|
while (*fmt) { |
|
|
|
@ -947,36 +960,16 @@ static unsigned char stbiw__paeth(int a, int b, int c) |
|
|
|
|
return STBIW_UCHAR(c); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifdef STB_IMAGE_WRITE_STATIC |
|
|
|
|
static int stbi_write_png_level = 8; |
|
|
|
|
#else |
|
|
|
|
int stbi_write_png_level = 8; |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
// @OPTIMIZE: provide an option that always forces left-predict or paeth predict
|
|
|
|
|
unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) |
|
|
|
|
static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) |
|
|
|
|
{ |
|
|
|
|
int ctype[5] = { -1, 0, 4, 2, 6 }; |
|
|
|
|
unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; |
|
|
|
|
unsigned char *out,*o, *filt, *zlib; |
|
|
|
|
signed char *line_buffer; |
|
|
|
|
int i,j,k,p,zlen; |
|
|
|
|
|
|
|
|
|
if (stride_bytes == 0) |
|
|
|
|
stride_bytes = x * n; |
|
|
|
|
|
|
|
|
|
filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; |
|
|
|
|
line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } |
|
|
|
|
for (j=0; j < y; ++j) { |
|
|
|
|
static int mapping[] = { 0,1,2,3,4 }; |
|
|
|
|
static int firstmap[] = { 0,1,0,5,6 }; |
|
|
|
|
int *mymap = (j != 0) ? mapping : firstmap; |
|
|
|
|
int best = 0, bestval = 0x7fffffff; |
|
|
|
|
for (p=0; p < 2; ++p) { |
|
|
|
|
for (k= p?best:0; k < 5; ++k) { // @TODO: clarity: rewrite this to go 0..5, and 'continue' the unwanted ones during 2nd pass
|
|
|
|
|
int type = mymap[k],est=0; |
|
|
|
|
unsigned char *z = pixels + stride_bytes*(stbi__flip_vertically_on_write ? y-1-j : j); |
|
|
|
|
for (i=0; i < n; ++i) |
|
|
|
|
int *mymap = (y != 0) ? mapping : firstmap; |
|
|
|
|
int i; |
|
|
|
|
int type = mymap[filter_type]; |
|
|
|
|
unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); |
|
|
|
|
for (i = 0; i < n; ++i) { |
|
|
|
|
switch (type) { |
|
|
|
|
case 0: line_buffer[i] = z[i]; break; |
|
|
|
|
case 1: line_buffer[i] = z[i]; break; |
|
|
|
@ -986,7 +979,8 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in |
|
|
|
|
case 5: line_buffer[i] = z[i]; break; |
|
|
|
|
case 6: line_buffer[i] = z[i]; break; |
|
|
|
|
} |
|
|
|
|
for (i=n; i < x*n; ++i) { |
|
|
|
|
} |
|
|
|
|
for (i=n; i < width*n; ++i) { |
|
|
|
|
switch (type) { |
|
|
|
|
case 0: line_buffer[i] = z[i]; break; |
|
|
|
|
case 1: line_buffer[i] = z[i] - z[i-n]; break; |
|
|
|
@ -997,18 +991,57 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in |
|
|
|
|
case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (p != 0) break; |
|
|
|
|
for (i=0; i < x*n; ++i) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) |
|
|
|
|
{ |
|
|
|
|
int force_filter = stbi_write_force_png_filter; |
|
|
|
|
int ctype[5] = { -1, 0, 4, 2, 6 }; |
|
|
|
|
unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; |
|
|
|
|
unsigned char *out,*o, *filt, *zlib; |
|
|
|
|
signed char *line_buffer; |
|
|
|
|
int j,zlen; |
|
|
|
|
|
|
|
|
|
if (stride_bytes == 0) |
|
|
|
|
stride_bytes = x * n; |
|
|
|
|
|
|
|
|
|
if (force_filter >= 5) { |
|
|
|
|
force_filter = -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; |
|
|
|
|
line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } |
|
|
|
|
for (j=0; j < y; ++j) { |
|
|
|
|
int filter_type; |
|
|
|
|
if (force_filter > -1) { |
|
|
|
|
filter_type = force_filter; |
|
|
|
|
stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, force_filter, line_buffer); |
|
|
|
|
} else { // Estimate the best filter by running through all of them:
|
|
|
|
|
int best_filter = 0, best_filter_val = 0x7fffffff, est, i; |
|
|
|
|
for (filter_type = 0; filter_type < 5; filter_type++) { |
|
|
|
|
stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, filter_type, line_buffer); |
|
|
|
|
|
|
|
|
|
// Estimate the entropy of the line using this filter; the less, the better.
|
|
|
|
|
est = 0; |
|
|
|
|
for (i = 0; i < x*n; ++i) { |
|
|
|
|
est += abs((signed char) line_buffer[i]); |
|
|
|
|
if (est < bestval) { bestval = est; best = k; } |
|
|
|
|
} |
|
|
|
|
if (est < best_filter_val) { |
|
|
|
|
best_filter_val = est; |
|
|
|
|
best_filter = filter_type; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it
|
|
|
|
|
stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, best_filter, line_buffer); |
|
|
|
|
filter_type = best_filter; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// when we get here, best contains the filter type, and line_buffer contains the data
|
|
|
|
|
filt[j*(x*n+1)] = (unsigned char) best; |
|
|
|
|
// when we get here, filter_type contains the filter type, and line_buffer contains the data
|
|
|
|
|
filt[j*(x*n+1)] = (unsigned char) filter_type; |
|
|
|
|
STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); |
|
|
|
|
} |
|
|
|
|
STBIW_FREE(line_buffer); |
|
|
|
|
zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_level); |
|
|
|
|
zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); |
|
|
|
|
STBIW_FREE(filt); |
|
|
|
|
if (!zlib) return 0; |
|
|
|
|
|
|
|
|
@ -1421,7 +1454,7 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const |
|
|
|
|
|
|
|
|
|
/* Revision history
|
|
|
|
|
1.08 (2018-01-29) |
|
|
|
|
add stbi__flip_vertically_on_write |
|
|
|
|
add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter |
|
|
|
|
1.07 (2017-07-24) |
|
|
|
|
doc fix |
|
|
|
|
1.06 (2017-07-23) |
|
|
|
|