mirror of
https://github.com/nothings/stb
synced 2024-12-16 04:42:42 +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
|
The PNG output is not optimal; it is 20-50% larger than the file
|
||||||
written by a decent optimizing implementation. This library is designed
|
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.
|
or run-time performance.
|
||||||
|
|
||||||
USAGE:
|
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_png_to_file(char const *filename, int x, int y, int comp, const void *data, int stride_bytes);
|
||||||
int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
|
int stbi_write_bmp_to_file(char const *filename, int x, int y, int comp, const void *data);
|
||||||
int stbi_write_tga(char const *filename, int w, int h, 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.
|
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
|
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
|
writer, both because it is in BGR order and because it may have padding
|
||||||
at the end of the line.)
|
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
|
#ifndef INCLUDE_STB_IMAGE_WRITE_H
|
||||||
@ -60,9 +77,22 @@ USAGE:
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
|
typedef struct
|
||||||
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);
|
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
|
#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
|
#ifdef STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
|
||||||
|
#ifndef STBI_WRITE_NO_STDIO
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif // STBI_WRITE_NO_STDIO
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.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 unsigned int stbiw_uint32;
|
||||||
typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];
|
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) {
|
while (*fmt) {
|
||||||
switch (*fmt++) {
|
switch (*fmt++) {
|
||||||
case ' ': break;
|
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];
|
case '2': { int x = va_arg(v,int); unsigned char b[2];
|
||||||
b[0] = (unsigned char) x; b[1] = (unsigned char) (x>>8);
|
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];
|
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[0]=(unsigned char)x; b[1]=(unsigned char)(x>>8);
|
||||||
b[2]=(unsigned char)(x>>16); b[3]=(unsigned char)(x>>24);
|
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:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
return;
|
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];
|
unsigned char arr[3];
|
||||||
arr[0] = a, arr[1] = b, arr[2] = c;
|
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];
|
unsigned char bg[3] = { 255, 0, 255}, px[3];
|
||||||
stbiw_uint32 zero = 0;
|
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) {
|
for (i=0; i < x; ++i) {
|
||||||
unsigned char *d = (unsigned char *) data + (j*x+i)*comp;
|
unsigned char *d = (unsigned char *) data + (j*x+i)*comp;
|
||||||
if (write_alpha < 0)
|
if (write_alpha < 0)
|
||||||
fwrite(&d[comp-1], 1, 1, f);
|
s->io.write(s->io_user_data, &d[comp-1], 1);
|
||||||
switch (comp) {
|
switch (comp) {
|
||||||
case 1:
|
case 1:
|
||||||
case 2: fwrite(d, 1, 1, f);
|
case 2: s->io.write(s->io_user_data, d, 1);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
if (!write_alpha) {
|
if (!write_alpha) {
|
||||||
// composite against pink background
|
// composite against pink background
|
||||||
for (k=0; k < 3; ++k)
|
for (k=0; k < 3; ++k)
|
||||||
px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
case 3:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
if (write_alpha > 0)
|
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;
|
if (y < 0 || x < 0) return 0;
|
||||||
f = fopen(filename, "wb");
|
va_list v;
|
||||||
if (f) {
|
va_start(v, fmt);
|
||||||
va_list v;
|
writefv(s, fmt, v);
|
||||||
va_start(v, fmt);
|
va_end(v);
|
||||||
writefv(f, fmt, v);
|
write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad);
|
||||||
va_end(v);
|
return 1;
|
||||||
write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad);
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
return f != NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
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",
|
"11 4 22 4" "4 44 22 444444",
|
||||||
'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header
|
'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
|
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 has_alpha = (comp == 2 || comp == 4);
|
||||||
int colorbytes = has_alpha ? comp-1 : comp;
|
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
|
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);
|
"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()
|
// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size()
|
||||||
#define stbiw__sbraw(a) ((int *) (a) - 2)
|
#define stbiw__sbraw(a) ((int *) (a) - 2)
|
||||||
#define stbiw__sbm(a) stbiw__sbraw(a)[0]
|
#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;
|
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;
|
int len;
|
||||||
unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);
|
unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);
|
||||||
if (!png) return 0;
|
if (!png) return 0;
|
||||||
f = fopen(filename, "wb");
|
int r = s->io.write(s->io_user_data, png, len);
|
||||||
if (!f) { free(png); return 0; }
|
|
||||||
fwrite(png, 1, len, f);
|
|
||||||
fclose(f);
|
|
||||||
free(png);
|
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
|
#endif // STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
|
||||||
/* Revision history
|
/* Revision history
|
||||||
|
Loading…
Reference in New Issue
Block a user