mirror of
https://github.com/nothings/stb
synced 2024-12-15 12:22:55 +03:00
Add support for writing through callback functions in stb_image_write.h.
This commit is contained in:
parent
a7c8694d69
commit
529d8163b2
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user