Add support for writing through callback functions in stb_image_write.h.

This commit is contained in:
Emmanuel Julien 2015-01-13 17:43:14 +01:00
parent a7c8694d69
commit 529d8163b2

View File

@ -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 <stdio.h>
#endif // STBI_WRITE_NO_STDIO
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
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