|
|
|
@ -24,11 +24,12 @@ ABOUT: |
|
|
|
|
|
|
|
|
|
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. |
|
|
|
|
|
|
|
|
@ -51,6 +52,10 @@ USAGE: |
|
|
|
|
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. |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
#ifndef INCLUDE_STB_IMAGE_WRITE_H |
|
|
|
@ -63,6 +68,7 @@ extern "C" { |
|
|
|
|
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 void *data); |
|
|
|
|
|
|
|
|
|
#ifdef __cplusplus |
|
|
|
|
} |
|
|
|
@ -185,6 +191,159 @@ int stbi_write_tga(char const *filename, int x, int y, int comp, const void *dat |
|
|
|
|
"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
|
|
|
|
|
// originally 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 { |
|
|
|
|
maxcomp = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; |
|
|
|
|
|
|
|
|
|
rgbe[0] = (unsigned char)(linear[0] * maxcomp); |
|
|
|
|
rgbe[1] = (unsigned char)(linear[1] * maxcomp); |
|
|
|
|
rgbe[2] = (unsigned char)(linear[2] * maxcomp); |
|
|
|
|
rgbe[3] = (unsigned char)(exponent + 128); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void stbiw__write_rle_data(FILE *f, int length, unsigned char databyte) |
|
|
|
|
{ |
|
|
|
|
unsigned char lengthbyte = 0x80 | (unsigned char)(length & 0x7f); |
|
|
|
|
fwrite(&lengthbyte, 1, 1, f); |
|
|
|
|
fwrite(&databyte, 1, 1, f); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void stbiw__write_nonrle_data(FILE *f, int length, unsigned char *data) |
|
|
|
|
{ |
|
|
|
|
unsigned char lengthbyte = (unsigned char )(length & 0xff); |
|
|
|
|
fwrite(&lengthbyte, 1, 1, f); |
|
|
|
|
fwrite(data, length, 1, f); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scratch, 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 { |
|
|
|
|
/* 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 (x=0; x < 4; x++) { |
|
|
|
|
unsigned char *comp = &scratch[width*x]; |
|
|
|
|
int runstart = 0, head = 0, rlerun = 0; |
|
|
|
|
|
|
|
|
|
while (head < width) { |
|
|
|
|
head++; |
|
|
|
|
|
|
|
|
|
if (head - runstart == 127 && rlerun == 1) { |
|
|
|
|
// max length RLE run
|
|
|
|
|
stbiw__write_rle_data(f, head - runstart, comp[runstart]); |
|
|
|
|
rlerun = 0; |
|
|
|
|
runstart = head; |
|
|
|
|
} else if (head - runstart == 128 && rlerun == 0) { |
|
|
|
|
// max length non-RLE run
|
|
|
|
|
stbiw__write_nonrle_data(f, head - runstart, comp+runstart); |
|
|
|
|
rlerun = 0; |
|
|
|
|
runstart = head; |
|
|
|
|
} else if (comp[head] != comp[head-1] && rlerun == 1) { |
|
|
|
|
// end of RLE run
|
|
|
|
|
stbiw__write_rle_data(f, head - runstart, comp[runstart]); |
|
|
|
|
rlerun = 0; |
|
|
|
|
runstart = head; |
|
|
|
|
} else { |
|
|
|
|
// continue accumulating RLE run
|
|
|
|
|
if (rlerun == 1) continue; |
|
|
|
|
|
|
|
|
|
// see if we can start an RLE run, at least 3 bytes same
|
|
|
|
|
if (rlerun == 0 && head - runstart >= 2 |
|
|
|
|
&& comp[head] == comp[head-1] |
|
|
|
|
&& comp[head] == comp[head-2]) { |
|
|
|
|
// found a run. Flush non-run (if there is anything) and then start an RLE run
|
|
|
|
|
if (head - runstart > 2) { |
|
|
|
|
stbiw__write_nonrle_data(f, head-2 - runstart, comp+runstart); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
rlerun = 1; |
|
|
|
|
runstart = head-2; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// flush remaining sequence (if any)
|
|
|
|
|
if (rlerun == 1) stbiw__write_rle_data(f, head - runstart, comp[runstart]); |
|
|
|
|
else if (head - runstart > 0) stbiw__write_nonrle_data(f, head - runstart, comp+runstart); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int stbi_write_hdr(char const *filename, int x, int y, int comp, const void *data) |
|
|
|
|
{ |
|
|
|
|
int i; |
|
|
|
|
FILE *f; |
|
|
|
|
unsigned char *scratch; |
|
|
|
|
if (y <= 0 || x <= 0) return 0; |
|
|
|
|
f = fopen(filename, "wb"); |
|
|
|
|
if (f) { |
|
|
|
|
float *scanline = (float *)data; |
|
|
|
|
/* Each component is stored separately. Allocate scratch space for full output scanline. */ |
|
|
|
|
scratch = (unsigned char *) malloc(x*4); if (!scanline) { fclose(f); return 0; } |
|
|
|
|
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, scanline + comp*i*x); |
|
|
|
|
free(scratch); |
|
|
|
|
fclose(f); |
|
|
|
|
} |
|
|
|
|
return f != NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 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] |
|
|
|
|