|
|
@ -1,16 +1,15 @@ |
|
|
|
/* stb_image_write - v0.95 - public domain - http://nothings.org/stb/stb_image_write.h |
|
|
|
/* stb_image_write - v0.98 - public domain - http://nothings.org/stb/stb_image_write.h |
|
|
|
writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010 |
|
|
|
no warranty implied; use at your own risk |
|
|
|
|
|
|
|
|
|
|
|
Before including, |
|
|
|
Before #including, |
|
|
|
|
|
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION |
|
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION |
|
|
|
|
|
|
|
in the file that you want to have the implementation. |
|
|
|
|
|
|
|
Will probably not work correctly with strict-aliasing optimizations. |
|
|
|
in the file that you want to have the implementation. |
|
|
|
|
|
|
|
Will probably not work correctly with strict-aliasing optimizations. |
|
|
|
|
|
|
|
ABOUT: |
|
|
|
|
|
|
@ -22,16 +21,24 @@ ABOUT: |
|
|
|
for source code compactness and simplicitly, not optimal image file size |
|
|
|
or run-time performance. |
|
|
|
|
|
|
|
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. |
|
|
|
You can define STBIW_MEMMOVE() to replace memmove() |
|
|
|
|
|
|
|
USAGE: |
|
|
|
|
|
|
|
There are three functions, one for each image file format: |
|
|
|
There are four functions, one for each image file format: |
|
|
|
|
|
|
|
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); |
|
|
|
int stbi_write_hdr(char const *filename, int w, int h, int comp, const void *data); |
|
|
|
|
|
|
|
Each function returns 0 on failure and non-0 on success. |
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
@ -44,13 +51,30 @@ USAGE: |
|
|
|
PNG creates output files with the same number of components as the input. |
|
|
|
The BMP format expands Y to RGB in the file format and does not |
|
|
|
output alpha. |
|
|
|
|
|
|
|
|
|
|
|
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.) |
|
|
|
|
|
|
|
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. |
|
|
|
|
|
|
|
CREDITS: |
|
|
|
|
|
|
|
PNG/BMP/TGA |
|
|
|
Sean Barrett |
|
|
|
HDR |
|
|
|
Baldur Karlsson |
|
|
|
TGA monochrome: |
|
|
|
Jean-Sebastien Guay |
|
|
|
misc enhancements: |
|
|
|
Tim Kelsey |
|
|
|
bugfixes: |
|
|
|
github:Chribba |
|
|
|
*/ |
|
|
|
|
|
|
|
#ifndef INCLUDE_STB_IMAGE_WRITE_H |
|
|
@ -60,9 +84,10 @@ USAGE: |
|
|
|
extern "C" { |
|
|
|
#endif |
|
|
|
|
|
|
|
extern int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); |
|
|
|
extern int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); |
|
|
|
extern int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); |
|
|
|
extern int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); |
|
|
|
extern int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); |
|
|
|
extern int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); |
|
|
|
extern int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); |
|
|
|
|
|
|
|
#ifdef __cplusplus |
|
|
|
} |
|
|
@ -76,7 +101,30 @@ extern int stbi_write_tga(char const *filename, int w, int h, int comp, const vo |
|
|
|
#include <stdlib.h> |
|
|
|
#include <stdio.h> |
|
|
|
#include <string.h> |
|
|
|
#include <math.h> |
|
|
|
|
|
|
|
#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && defined(STBIW_REALLOC) |
|
|
|
// ok |
|
|
|
#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) |
|
|
|
// ok |
|
|
|
#else |
|
|
|
#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC." |
|
|
|
#endif |
|
|
|
|
|
|
|
#ifndef STBIW_MALLOC |
|
|
|
#define STBIW_MALLOC(sz) malloc(sz) |
|
|
|
#define STBIW_REALLOC(p,sz) realloc(p,sz) |
|
|
|
#define STBIW_FREE(p) free(p) |
|
|
|
#endif |
|
|
|
#ifndef STBIW_MEMMOVE |
|
|
|
#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
#ifndef STBIW_ASSERT |
|
|
|
#include <assert.h> |
|
|
|
#define STBIW_ASSERT(x) assert(x) |
|
|
|
#endif |
|
|
|
|
|
|
|
typedef unsigned int stbiw_uint32; |
|
|
|
typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; |
|
|
@ -95,7 +143,7 @@ static void writefv(FILE *f, const char *fmt, va_list v) |
|
|
|
b[2]=(unsigned char)(x>>16); b[3]=(unsigned char)(x>>24); |
|
|
|
fwrite(b,4,1,f); break; } |
|
|
|
default: |
|
|
|
assert(0); |
|
|
|
STBIW_ASSERT(0); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
@ -108,7 +156,7 @@ static void write3(FILE *f, unsigned char a, unsigned char b, unsigned char c) |
|
|
|
fwrite(arr, 3, 1, f); |
|
|
|
} |
|
|
|
|
|
|
|
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) |
|
|
|
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, int expand_mono) |
|
|
|
{ |
|
|
|
unsigned char bg[3] = { 255, 0, 255}, px[3]; |
|
|
|
stbiw_uint32 zero = 0; |
|
|
@ -117,7 +165,7 @@ static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, |
|
|
|
if (y <= 0) |
|
|
|
return; |
|
|
|
|
|
|
|
if (vdir < 0) |
|
|
|
if (vdir < 0) |
|
|
|
j_end = -1, j = y-1; |
|
|
|
else |
|
|
|
j_end = y, j = 0; |
|
|
@ -128,8 +176,12 @@ static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, |
|
|
|
if (write_alpha < 0) |
|
|
|
fwrite(&d[comp-1], 1, 1, f); |
|
|
|
switch (comp) { |
|
|
|
case 1: |
|
|
|
case 2: fwrite(d, 1, 1, f); |
|
|
|
case 1: fwrite(d, 1, 1, f); |
|
|
|
break; |
|
|
|
case 2: if (expand_mono) |
|
|
|
write3(f, d[0],d[0],d[0]); // monochrome bmp |
|
|
|
else |
|
|
|
fwrite(d, 1, 1, f); // monochrome TGA |
|
|
|
break; |
|
|
|
case 4: |
|
|
|
if (!write_alpha) { |
|
|
@ -151,7 +203,7 @@ static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
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, ...) |
|
|
|
static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) |
|
|
|
{ |
|
|
|
FILE *f; |
|
|
|
if (y < 0 || x < 0) return 0; |
|
|
@ -161,7 +213,7 @@ static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, in |
|
|
|
va_start(v, fmt); |
|
|
|
writefv(f, fmt, v); |
|
|
|
va_end(v); |
|
|
|
write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad); |
|
|
|
write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad,expand_mono); |
|
|
|
fclose(f); |
|
|
|
} |
|
|
|
return f != NULL; |
|
|
@ -170,7 +222,7 @@ static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, in |
|
|
|
int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) |
|
|
|
{ |
|
|
|
int pad = (-x*3) & 3; |
|
|
|
return outfile(filename,-1,-1,x,y,comp,(void *) data,0,pad, |
|
|
|
return outfile(filename,-1,-1,x,y,comp,mi">1,(void *) data,0,pad, |
|
|
|
"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 |
|
|
@ -181,10 +233,159 @@ int stbi_write_tga(char const *filename, int x, int y, int comp, const void *dat |
|
|
|
int has_alpha = (comp == 2 || comp == 4); |
|
|
|
int colorbytes = has_alpha ? comp-1 : comp; |
|
|
|
int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 |
|
|
|
return outfile(filename, -1,-1, x, y, comp, (void *) data, has_alpha, 0, |
|
|
|
return outfile(filename, -1,-1, x, y, comp, mi">0, (void *) data, has_alpha, 0, |
|
|
|
"111 221 2222 11", 0,0,format, 0,0,0, 0,0,x,y, (colorbytes+has_alpha)*8, has_alpha*8); |
|
|
|
} |
|
|
|
|
|
|
|
// ************************************************************************************************* |
|
|
|
// Radiance RGBE HDR writer |
|
|
|
// by Baldur Karlsson |
|
|
|
#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) |
|
|
|
|
|
|
|
void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) |
|
|
|
{ |
|
|
|
int exponent; |
|
|
|
float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); |
|
|
|
|
|
|
|
if (maxcomp < 1e-32) { |
|
|
|
rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; |
|
|
|
} else { |
|
|
|
float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; |
|
|
|
|
|
|
|
rgbe[0] = (unsigned char)(linear[0] * normalize); |
|
|
|
rgbe[1] = (unsigned char)(linear[1] * normalize); |
|
|
|
rgbe[2] = (unsigned char)(linear[2] * normalize); |
|
|
|
rgbe[3] = (unsigned char)(exponent + 128); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void stbiw__write_run_data(FILE *f, int length, unsigned char databyte) |
|
|
|
{ |
|
|
|
unsigned char lengthbyte = (unsigned char) (length+128); |
|
|
|
STBIW_ASSERT(length+128 <= 255); |
|
|
|
fwrite(&lengthbyte, 1, 1, f); |
|
|
|
fwrite(&databyte, 1, 1, f); |
|
|
|
} |
|
|
|
|
|
|
|
void stbiw__write_dump_data(FILE *f, int length, unsigned char *data) |
|
|
|
{ |
|
|
|
unsigned char lengthbyte = (unsigned char )(length & 0xff); |
|
|
|
STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code |
|
|
|
fwrite(&lengthbyte, 1, 1, f); |
|
|
|
fwrite(data, length, 1, f); |
|
|
|
} |
|
|
|
|
|
|
|
void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scratch, const float *scanline) |
|
|
|
{ |
|
|
|
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++) { |
|
|
|
switch (comp) { |
|
|
|
case 4: /* fallthrough */ |
|
|
|
case 3: linear[2] = scanline[x*comp + 2]; |
|
|
|
linear[1] = scanline[x*comp + 1]; |
|
|
|
linear[0] = scanline[x*comp + 0]; |
|
|
|
break; |
|
|
|
case 2: /* fallthrough */ |
|
|
|
case 1: linear[0] = linear[1] = linear[2] = scanline[x*comp + 0]; |
|
|
|
break; |
|
|
|
} |
|
|
|
stbiw__linear_to_rgbe(rgbe, linear); |
|
|
|
fwrite(rgbe, 4, 1, f); |
|
|
|
} |
|
|
|
} else { |
|
|
|
int c,r; |
|
|
|
/* encode into scratch buffer */ |
|
|
|
for (x=0; x < width; x++) { |
|
|
|
switch(comp) { |
|
|
|
case 4: /* fallthrough */ |
|
|
|
case 3: linear[2] = scanline[x*comp + 2]; |
|
|
|
linear[1] = scanline[x*comp + 1]; |
|
|
|
linear[0] = scanline[x*comp + 0]; |
|
|
|
break; |
|
|
|
case 2: /* fallthrough */ |
|
|
|
case 1: linear[0] = linear[1] = linear[2] = scanline[x*comp + 0]; |
|
|
|
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]; |
|
|
|
} |
|
|
|
|
|
|
|
fwrite(scanlineheader, 4, 1, f); |
|
|
|
|
|
|
|
/* RLE each component separately */ |
|
|
|
for (c=0; c < 4; c++) { |
|
|
|
unsigned char *comp = &scratch[width*c]; |
|
|
|
|
|
|
|
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; |
|
|
|
stbiw__write_dump_data(f, len, &comp[x]); |
|
|
|
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]) |
|
|
|
++r; |
|
|
|
// output run up to r |
|
|
|
while (x < r) { |
|
|
|
int len = r-x; |
|
|
|
if (len > 127) len = 127; |
|
|
|
stbiw__write_run_data(f, len, comp[x]); |
|
|
|
x += len; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) |
|
|
|
{ |
|
|
|
int i; |
|
|
|
FILE *f; |
|
|
|
if (y <= 0 || x <= 0 || data == NULL) return 0; |
|
|
|
f = fopen(filename, "wb"); |
|
|
|
if (f) { |
|
|
|
/* Each component is stored separately. Allocate scratch space for full output scanline. */ |
|
|
|
unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); |
|
|
|
fprintf(f, "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n" ); |
|
|
|
fprintf(f, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n" , y, x); |
|
|
|
for(i=0; i < y; i++) |
|
|
|
stbiw__write_hdr_scanline(f, x, comp, scratch, data + comp*i*x); |
|
|
|
STBIW_FREE(scratch); |
|
|
|
fclose(f); |
|
|
|
} |
|
|
|
return f != NULL; |
|
|
|
} |
|
|
|
|
|
|
|
///////////////////////////////////////////////////////// |
|
|
|
// PNG |
|
|
|
|
|
|
|
// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() |
|
|
|
#define stbiw__sbraw(a) ((int *) (a) - 2) |
|
|
|
#define stbiw__sbm(a) stbiw__sbraw(a)[0] |
|
|
@ -196,13 +397,13 @@ int stbi_write_tga(char const *filename, int x, int y, int comp, const void *dat |
|
|
|
|
|
|
|
#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) |
|
|
|
#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) |
|
|
|
#define stbiw__sbfree(a) ((a) ? free(stbiw__sbraw(a)),0 : 0) |
|
|
|
#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) |
|
|
|
|
|
|
|
static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) |
|
|
|
{ |
|
|
|
int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; |
|
|
|
void *p = realloc(*arr ? stbiw__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2); |
|
|
|
assert(p); |
|
|
|
void *p = STBIW_REALLOC(*arr ? stbiw__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2); |
|
|
|
STBIW_ASSERT(p); |
|
|
|
if (p) { |
|
|
|
if (!*arr) ((int *) p)[1] = 0; |
|
|
|
*arr = (void *) ((int *) p + 2); |
|
|
@ -287,7 +488,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l |
|
|
|
|
|
|
|
i=0; |
|
|
|
while (i < data_len-3) { |
|
|
|
// hash next 3 bytes of data to be compressed |
|
|
|
// hash next 3 bytes of data to be compressed |
|
|
|
int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; |
|
|
|
unsigned char *bestloc = 0; |
|
|
|
unsigned char **hlist = hash_table[h]; |
|
|
@ -300,7 +501,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l |
|
|
|
} |
|
|
|
// when hash table entry is too long, delete half the entries |
|
|
|
if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { |
|
|
|
memcpy(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); |
|
|
|
STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); |
|
|
|
stbiw__sbn(hash_table[h]) = quality; |
|
|
|
} |
|
|
|
stbiw__sbpush(hash_table[h],data+i); |
|
|
@ -323,7 +524,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l |
|
|
|
|
|
|
|
if (bestloc) { |
|
|
|
int d = (int) (data+i - bestloc); // distance back |
|
|
|
assert(d <= 32767 && best <= 258); |
|
|
|
STBIW_ASSERT(d <= 32767 && best <= 258); |
|
|
|
for (j=0; best > lengthc[j+1]-1; ++j); |
|
|
|
stbiw__zlib_huff(j+257); |
|
|
|
if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); |
|
|
@ -364,7 +565,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l |
|
|
|
} |
|
|
|
*out_len = stbiw__sbn(out); |
|
|
|
// make returned pointer freeable |
|
|
|
memmove(stbiw__sbraw(out), out, *out_len); |
|
|
|
STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); |
|
|
|
return (unsigned char *) stbiw__sbraw(out); |
|
|
|
} |
|
|
|
|
|
|
@ -411,8 +612,8 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in |
|
|
|
if (stride_bytes == 0) |
|
|
|
stride_bytes = x * n; |
|
|
|
|
|
|
|
filt = (unsigned char *) malloc((x*n+1) * y); if (!filt) return 0; |
|
|
|
line_buffer = (signed char *) malloc(x * n); if (!line_buffer) { free(filt); return 0; } |
|
|
|
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 }; |
|
|
@ -451,20 +652,20 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in |
|
|
|
} |
|
|
|
// when we get here, best contains the filter type, and line_buffer contains the data |
|
|
|
filt[j*(x*n+1)] = (unsigned char) best; |
|
|
|
memcpy(filt+j*(x*n+1)+1, line_buffer, x*n); |
|
|
|
STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); |
|
|
|
} |
|
|
|
free(line_buffer); |
|
|
|
STBIW_FREE(line_buffer); |
|
|
|
zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory |
|
|
|
free(filt); |
|
|
|
STBIW_FREE(filt); |
|
|
|
if (!zlib) return 0; |
|
|
|
|
|
|
|
// each tag requires 12 bytes of overhead |
|
|
|
out = (unsigned char *) malloc(8 + 12+13 + 12+zlen + 12); |
|
|
|
out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); |
|
|
|
if (!out) return 0; |
|
|
|
*out_len = 8 + 12+13 + 12+zlen + 12; |
|
|
|
|
|
|
|
o=out; |
|
|
|
memcpy(o,sig,8); o+= 8; |
|
|
|
STBIW_MEMMOVE(o,sig,8); o+= 8; |
|
|
|
stbiw__wp32(o, 13); // header length |
|
|
|
stbiw__wptag(o, "IHDR"); |
|
|
|
stbiw__wp32(o, x); |
|
|
@ -478,14 +679,16 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in |
|
|
|
|
|
|
|
stbiw__wp32(o, zlen); |
|
|
|
stbiw__wptag(o, "IDAT"); |
|
|
|
memcpy(o, zlib, zlen); o += zlen; free(zlib); |
|
|
|
STBIW_MEMMOVE(o, zlib, zlen); |
|
|
|
o += zlen; |
|
|
|
STBIW_FREE(zlib); |
|
|
|
stbiw__wpcrc(&o, zlen); |
|
|
|
|
|
|
|
stbiw__wp32(o,0); |
|
|
|
stbiw__wptag(o, "IEND"); |
|
|
|
stbiw__wpcrc(&o,0); |
|
|
|
|
|
|
|
assert(o == out + *out_len); |
|
|
|
STBIW_ASSERT(o == out + *out_len); |
|
|
|
|
|
|
|
return out; |
|
|
|
} |
|
|
@ -497,16 +700,22 @@ int stbi_write_png(char const *filename, int x, int y, int comp, const void *dat |
|
|
|
unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); |
|
|
|
if (!png) return 0; |
|
|
|
f = fopen(filename, "wb"); |
|
|
|
if (!f) { free(png); return 0; } |
|
|
|
if (!f) { STBIW_FREE(png); return 0; } |
|
|
|
fwrite(png, 1, len, f); |
|
|
|
fclose(f); |
|
|
|
free(png); |
|
|
|
STBIW_FREE(png); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
#endif // STB_IMAGE_WRITE_IMPLEMENTATION |
|
|
|
|
|
|
|
/* Revision history |
|
|
|
|
|
|
|
0.98 (2015-04-08) |
|
|
|
added STBIW_MALLOC, STBIW_ASSERT etc |
|
|
|
0.97 (2015-01-18) |
|
|
|
fixed HDR asserts, rewrote HDR rle logic |
|
|
|
0.96 (2015-01-17) |
|
|
|
add HDR output |
|
|
|
fix monochrome BMP |
|
|
|
0.95 (2014-08-17) |
|
|
|
add monochrome TGA output |
|
|
|
0.94 (2014-05-31) |
|
|
|