aboutsummaryrefslogtreecommitdiff
path: root/src/stb_image_write.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/stb_image_write.h')
-rw-r--r--src/stb_image_write.h289
1 files changed, 249 insertions, 40 deletions
diff --git a/src/stb_image_write.h b/src/stb_image_write.h
index da3f7e22..10489707 100644
--- a/src/stb_image_write.h
+++ b/src/stb_image_write.h
@@ -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,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, 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)