From 529d8163b236e526dccb8640727a71b438febc8f Mon Sep 17 00:00:00 2001 From: Emmanuel Julien Date: Tue, 13 Jan 2015 17:43:14 +0100 Subject: [PATCH] Add support for writing through callback functions in stb_image_write.h. --- stb_image_write.h | 211 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 166 insertions(+), 45 deletions(-) diff --git a/stb_image_write.h b/stb_image_write.h index da3f7e2..364932f 100644 --- a/stb_image_write.h +++ b/stb_image_write.h @@ -19,16 +19,23 @@ ABOUT: The PNG output is not optimal; it is 20-50% larger than the file written by a decent optimizing implementation. This library is designed - for source code compactness and simplicitly, not optimal image file size + for source code compactness and simplicity, not optimal image file size or run-time performance. USAGE: - There are three functions, one for each image file format: + There are functions for each image file format and output method: - 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_png_to_file(char const *filename, int x, int y, int comp, const void *data, int stride_bytes); + int stbi_write_bmp_to_file(char const *filename, int x, int y, int comp, const void *data); + int stbi_write_tga_to_file(char const *filename, int x, int y, int comp, const void *data); + + int stbi_write_png_to_callbacks(stbi_io_write_callbacks const *clbk, void *user, int x, int y, int comp, const void *data, int stride_bytes); + int stbi_write_bmp_to_callbacks(stbi_io_write_callbacks const *clbk, void *user, int x, int y, int comp, const void *data); + int stbi_write_tga_to_callbacks(stbi_io_write_callbacks const *clbk, void *user, int x, int y, int comp, const void *data); + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions. Each function returns 0 on failure and non-0 on success. @@ -51,6 +58,16 @@ 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.) + + =========================================================================== + + I/O callbacks + + I/O callbacks allow you to write to arbitrary sources, like packaged + files or some other source. + + The function you must define is "write" (writes some bytes of data). + */ #ifndef INCLUDE_STB_IMAGE_WRITE_H @@ -60,9 +77,22 @@ 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); +typedef struct +{ + int (*write) (void *user,void *data,int size); // write 'size' bytes from 'data'. return number of bytes actually written +} stbi_io_write_callbacks; + +extern int stbi_write_png_to_callbacks(stbi_io_write_callbacks const *clbk, void *user, int x, int y, int comp, const void *data, int stride_bytes); +extern int stbi_write_bmp_to_callbacks(stbi_io_write_callbacks const *clbk, void *user, int x, int y, int comp, const void *data); +extern int stbi_write_tga_to_callbacks(stbi_io_write_callbacks const *clbk, void *user, int x, int y, int comp, const void *data); + +#ifndef STBI_WRITE_NO_STDIO + +extern int stbi_write_png_to_file(char const *filename, int x, int y, int comp, const void *data, int stride_bytes); +extern int stbi_write_bmp_to_file(char const *filename, int x, int y, int comp, const void *data); +extern int stbi_write_tga_to_file(char const *filename, int x, int y, int comp, const void *data); + +#endif // !STBI_WRITE_NO_STDIO #ifdef __cplusplus } @@ -72,28 +102,68 @@ extern int stbi_write_tga(char const *filename, int w, int h, int comp, const vo #ifdef STB_IMAGE_WRITE_IMPLEMENTATION +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO #include #include -#include #include #include +typedef struct +{ + stbi_io_write_callbacks io; + void *io_user_data; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_io_write_callbacks const *c, void *user) +{ + s->io = *c; + s->io_user_data = user; +} + +#ifndef STBI_WRITE_NO_STDIO + +static int stbi__stdio_write(void *user, void *data, int size) +{ + return (int) fwrite(data,1,size,(FILE*) user); +} + +static stbi_io_write_callbacks stbi__stdio_write_callbacks = +{ + stbi__stdio_write +}; + +static void stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = fopen(filename, "wb"); + stbi__start_write_callbacks(s, &stbi__stdio_write_callbacks, (void *) f); +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->io_user_data); +} + +#endif // !STBI_WRITE_NO_STDIO + typedef unsigned int stbiw_uint32; typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; -static void writefv(FILE *f, const char *fmt, va_list v) +static void writefv(stbi__write_context const *s, const char *fmt, va_list v) { while (*fmt) { switch (*fmt++) { case ' ': break; - case '1': { unsigned char x = (unsigned char) va_arg(v, int); fputc(x,f); break; } + case '1': { unsigned char x = (unsigned char) va_arg(v, int); s->io.write(s->io_user_data,&x,1); break; } case '2': { int x = va_arg(v,int); unsigned char b[2]; b[0] = (unsigned char) x; b[1] = (unsigned char) (x>>8); - fwrite(b,2,1,f); break; } + s->io.write(s->io_user_data,b,2); break; } case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4]; b[0]=(unsigned char)x; b[1]=(unsigned char)(x>>8); b[2]=(unsigned char)(x>>16); b[3]=(unsigned char)(x>>24); - fwrite(b,4,1,f); break; } + s->io.write(s->io_user_data,b,4); break; } default: assert(0); return; @@ -101,14 +171,14 @@ static void writefv(FILE *f, const char *fmt, va_list v) } } -static void write3(FILE *f, unsigned char a, unsigned char b, unsigned char c) +static void write3(stbi__write_context const *s, unsigned char a, unsigned char b, unsigned char c) { unsigned char arr[3]; arr[0] = a, arr[1] = b, arr[2] = c; - fwrite(arr, 3, 1, f); + s->io.write(s->io_user_data, arr, 3); } -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(stbi__write_context const *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad) { unsigned char bg[3] = { 255, 0, 255}, px[3]; stbiw_uint32 zero = 0; @@ -126,65 +196,100 @@ static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, for (i=0; i < x; ++i) { unsigned char *d = (unsigned char *) data + (j*x+i)*comp; if (write_alpha < 0) - fwrite(&d[comp-1], 1, 1, f); + s->io.write(s->io_user_data, &d[comp-1], 1); switch (comp) { case 1: - case 2: fwrite(d, 1, 1, f); + case 2: s->io.write(s->io_user_data, d, 1); 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; - write3(f, px[1-rgb_dir],px[1],px[1+rgb_dir]); + write3(s, px[1-rgb_dir],px[1],px[1+rgb_dir]); break; } /* FALLTHROUGH */ case 3: - write3(f, d[1-rgb_dir],d[1],d[1+rgb_dir]); + write3(s, d[1-rgb_dir],d[1],d[1+rgb_dir]); break; } if (write_alpha > 0) - fwrite(&d[comp-1], 1, 1, f); + s->io.write(s->io_user_data, &d[comp-1], 1); } - fwrite(&zero,scanline_pad,1,f); + s->io.write(s->io_user_data,&zero,scanline_pad); } } -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(stbi__write_context const *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, const char *fmt, ...) { - FILE *f; if (y < 0 || x < 0) return 0; - f = fopen(filename, "wb"); - if (f) { - va_list v; - va_start(v, fmt); - writefv(f, fmt, v); - va_end(v); - write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad); - fclose(f); - } - return f != NULL; + va_list v; + va_start(v, fmt); + writefv(s, fmt, v); + va_end(v); + write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad); + return 1; } -int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +int stbi_write_bmp(stbi__write_context const *s, 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(s,-1,-1,x,y,comp,(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 } -int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +int stbi_write_bmp_to_callbacks(stbi_io_write_callbacks const *clbk, void *user, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, clbk, user); + return stbi_write_bmp(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO + +int stbi_write_bmp_to_file(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_file(&s,filename); + int r = stbi_write_bmp(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; +} + +#endif //!STBI_WRITE_NO_STDIO + +int stbi_write_tga(stbi__write_context const *s, int x, int y, int comp, const void *data) { 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(s, -1,-1, x, y, comp, (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); } +int stbi_write_tga_to_callbacks(stbi_io_write_callbacks const *clbk, void *user, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, clbk, user); + return stbi_write_tga(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO + +int stbi_write_tga_to_file(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_file(&s,filename); + int r = stbi_write_tga(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; +} + +#endif //!STBI_WRITE_NO_STDIO + // 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] @@ -490,19 +595,35 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in return out; } -int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +int stbi_write_png(stbi__write_context const *s, int x, int y, int comp, const void *data, int stride_bytes) { - FILE *f; int len; 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; } - fwrite(png, 1, len, f); - fclose(f); + int r = s->io.write(s->io_user_data, png, len); free(png); - return 1; + return r; } + +int stbi_write_png_to_callbacks(stbi_io_write_callbacks const *clbk, void *user, int x, int y, int comp, const void *data, int stride_bytes) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, clbk, user); + return stbi_write_png(&s, x, y, comp, data, stride_bytes); +} + +#ifndef STBI_WRITE_NO_STDIO + +int stbi_write_png_to_file(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + stbi__write_context s; + stbi__start_write_file(&s,filename); + int r = stbi_write_png(&s, x, y, comp, data, stride_bytes); + stbi__end_write_file(&s); + return r; +} +#endif //!STBI_WRITE_NO_STDIO + #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history