瀏覽代碼

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

Emmanuel Julien 10 年之前
父節點
當前提交
529d8163b2
共有 1 個文件被更改,包括 166 次插入45 次删除
  1. 166 45
      stb_image_write.h

+ 166 - 45
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 <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