|
@@ -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
|