2
0
Эх сурвалжийг харах

Merge branch 'master' of https://github.com/nothings/stb

Ken Miller 10 жил өмнө
parent
commit
bdc918751d
100 өөрчлөгдсөн 2242 нэмэгдсэн , 137 устгасан
  1. 3 2
      README.md
  2. 230 84
      stb_image.h
  3. 40 22
      stb_rect_pack.h
  4. 8 8
      stb_tilemap_editor.h
  5. 522 9
      stb_truetype.h
  6. 2 1
      tests/c_lexer_test.dsp
  7. 46 9
      tests/image_test.c
  8. 3 2
      tests/image_test.dsp
  9. 94 0
      tests/oversample/README.md
  10. 332 0
      tests/oversample/main.c
  11. 97 0
      tests/oversample/oversample.dsp
  12. 29 0
      tests/oversample/oversample.dsw
  13. BIN
      tests/oversample/oversample.exe
  14. 827 0
      tests/oversample/stb_wingraph.h
  15. BIN
      tests/pngsuite/16bit/basi0g16.png
  16. BIN
      tests/pngsuite/16bit/basi2c16.png
  17. BIN
      tests/pngsuite/16bit/basi4a16.png
  18. BIN
      tests/pngsuite/16bit/basi6a16.png
  19. BIN
      tests/pngsuite/16bit/basn0g16.png
  20. BIN
      tests/pngsuite/16bit/basn2c16.png
  21. BIN
      tests/pngsuite/16bit/basn4a16.png
  22. BIN
      tests/pngsuite/16bit/basn6a16.png
  23. BIN
      tests/pngsuite/16bit/bgai4a16.png
  24. BIN
      tests/pngsuite/16bit/bgan6a16.png
  25. BIN
      tests/pngsuite/16bit/bggn4a16.png
  26. BIN
      tests/pngsuite/16bit/bgyn6a16.png
  27. BIN
      tests/pngsuite/16bit/oi1n0g16.png
  28. BIN
      tests/pngsuite/16bit/oi1n2c16.png
  29. BIN
      tests/pngsuite/16bit/oi2n0g16.png
  30. BIN
      tests/pngsuite/16bit/oi2n2c16.png
  31. BIN
      tests/pngsuite/16bit/oi4n0g16.png
  32. BIN
      tests/pngsuite/16bit/oi4n2c16.png
  33. BIN
      tests/pngsuite/16bit/oi9n0g16.png
  34. BIN
      tests/pngsuite/16bit/oi9n2c16.png
  35. BIN
      tests/pngsuite/16bit/tbbn2c16.png
  36. BIN
      tests/pngsuite/16bit/tbgn2c16.png
  37. BIN
      tests/pngsuite/16bit/tbwn0g16.png
  38. 9 0
      tests/pngsuite/PngSuite.LICENSE
  39. BIN
      tests/pngsuite/corrupt/xc1n0g08.png
  40. BIN
      tests/pngsuite/corrupt/xc9n2c08.png
  41. BIN
      tests/pngsuite/corrupt/xcrn0g04.png
  42. BIN
      tests/pngsuite/corrupt/xcsn0g01.png
  43. BIN
      tests/pngsuite/corrupt/xd0n2c08.png
  44. BIN
      tests/pngsuite/corrupt/xd3n2c08.png
  45. BIN
      tests/pngsuite/corrupt/xd9n2c08.png
  46. BIN
      tests/pngsuite/corrupt/xdtn0g01.png
  47. BIN
      tests/pngsuite/corrupt/xhdn0g08.png
  48. BIN
      tests/pngsuite/corrupt/xlfn0g04.png
  49. BIN
      tests/pngsuite/corrupt/xs1n0g01.png
  50. BIN
      tests/pngsuite/corrupt/xs2n0g01.png
  51. BIN
      tests/pngsuite/corrupt/xs4n0g01.png
  52. BIN
      tests/pngsuite/corrupt/xs7n0g01.png
  53. BIN
      tests/pngsuite/primary/basi0g01.png
  54. BIN
      tests/pngsuite/primary/basi0g02.png
  55. BIN
      tests/pngsuite/primary/basi0g04.png
  56. BIN
      tests/pngsuite/primary/basi0g08.png
  57. BIN
      tests/pngsuite/primary/basi2c08.png
  58. BIN
      tests/pngsuite/primary/basi3p01.png
  59. BIN
      tests/pngsuite/primary/basi3p02.png
  60. BIN
      tests/pngsuite/primary/basi3p04.png
  61. BIN
      tests/pngsuite/primary/basi3p08.png
  62. BIN
      tests/pngsuite/primary/basi4a08.png
  63. BIN
      tests/pngsuite/primary/basi6a08.png
  64. BIN
      tests/pngsuite/primary/basn0g01.png
  65. BIN
      tests/pngsuite/primary/basn0g02.png
  66. BIN
      tests/pngsuite/primary/basn0g04.png
  67. BIN
      tests/pngsuite/primary/basn0g08.png
  68. BIN
      tests/pngsuite/primary/basn2c08.png
  69. BIN
      tests/pngsuite/primary/basn3p01.png
  70. BIN
      tests/pngsuite/primary/basn3p02.png
  71. BIN
      tests/pngsuite/primary/basn3p04.png
  72. BIN
      tests/pngsuite/primary/basn3p08.png
  73. BIN
      tests/pngsuite/primary/basn4a08.png
  74. BIN
      tests/pngsuite/primary/basn6a08.png
  75. BIN
      tests/pngsuite/primary/bgai4a08.png
  76. BIN
      tests/pngsuite/primary/bgan6a08.png
  77. BIN
      tests/pngsuite/primary/bgbn4a08.png
  78. BIN
      tests/pngsuite/primary/bgwn6a08.png
  79. BIN
      tests/pngsuite/primary/s01i3p01.png
  80. BIN
      tests/pngsuite/primary/s01n3p01.png
  81. BIN
      tests/pngsuite/primary/s02i3p01.png
  82. BIN
      tests/pngsuite/primary/s02n3p01.png
  83. BIN
      tests/pngsuite/primary/s03i3p01.png
  84. BIN
      tests/pngsuite/primary/s03n3p01.png
  85. BIN
      tests/pngsuite/primary/s04i3p01.png
  86. BIN
      tests/pngsuite/primary/s04n3p01.png
  87. BIN
      tests/pngsuite/primary/s05i3p02.png
  88. BIN
      tests/pngsuite/primary/s05n3p02.png
  89. BIN
      tests/pngsuite/primary/s06i3p02.png
  90. BIN
      tests/pngsuite/primary/s06n3p02.png
  91. BIN
      tests/pngsuite/primary/s07i3p02.png
  92. BIN
      tests/pngsuite/primary/s07n3p02.png
  93. BIN
      tests/pngsuite/primary/s08i3p02.png
  94. BIN
      tests/pngsuite/primary/s08n3p02.png
  95. BIN
      tests/pngsuite/primary/s09i3p02.png
  96. BIN
      tests/pngsuite/primary/s09n3p02.png
  97. BIN
      tests/pngsuite/primary/s32i3p04.png
  98. BIN
      tests/pngsuite/primary/s32n3p04.png
  99. BIN
      tests/pngsuite/primary/s33i3p04.png
  100. BIN
      tests/pngsuite/primary/s33n3p04.png

+ 3 - 2
README.md

@@ -6,10 +6,11 @@ single-file public domain libraries for C/C++
 library    | lastest version | category | description
 --------------------- | ---- | -------- | --------------------------------
 **stb_vorbis.c** | 1.04 | audio | decode ogg vorbis files from file/memory to float/16-bit signed output
-**stb_image.h** | 1.46 | graphics | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
-**stb_truetype.h** | 0.99 | graphics | parse, decode, and rasterize characters from truetype fonts
+**stb_image.h** | 1.48 | graphics | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
+**stb_truetype.h** | 1.02 | graphics | parse, decode, and rasterize characters from truetype fonts
 **stb_image_write.h** | 0.95 | graphics | image writing to disk: PNG, TGA, BMP
 **stb_image_resize.h** | 0.90 | graphics | resize images larger/smaller with good quality
+**stb_rect_pack.h** | 0.05 | graphics | simple 2D rectangle packer with decent quality
 **stretchy_buffer.h** | 1.01 | utility | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++
 **stb_textedit.h** | 1.5 | UI | guts of a text editor for games etc implementing them from scratch
 **stb_dxt.h** | 1.04 | 3D&nbsp;graphics | Fabian "ryg" Giesen's real-time DXT compressor

+ 230 - 84
stb_image.h

@@ -1,4 +1,4 @@
-/* stb_image - v1.46 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c
+/* stb_image - v1.48 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c
    when you control the images you're loading
                                      no warranty implied; use at your own risk
 
@@ -13,7 +13,7 @@
           avoid problematic images and only need the trivial interface
 
       JPEG baseline (no JPEG progressive)
-      PNG 8-bit-per-channel only
+      PNG 1/2/4/8-bit-per-channel (16 bpc not supported)
 
       TGA (not sure what subset, if a subset)
       BMP non-1bpp, non-RLE
@@ -28,18 +28,16 @@
       - overridable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD)
 
    Latest revisions:
+      1.48 (2014-12-14) fix incorrectly-named assert()
+      1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted)
+                        optimize PNG
+                        fix bug in interlaced PNG with user-specified channel count
       1.46 (2014-08-26) fix broken tRNS chunk in non-paletted PNG
       1.45 (2014-08-16) workaround MSVC-ARM internal compiler error by wrapping malloc
       1.44 (2014-08-07) warnings
       1.43 (2014-07-15) fix MSVC-only bug in 1.42
       1.42 (2014-07-09) no _CRT_SECURE_NO_WARNINGS; error-path fixes; STBI_ASSERT
       1.41 (2014-06-25) fix search&replace that messed up comments/error messages
-      1.40 (2014-06-22) gcc warning
-      1.39 (2014-06-15) TGA optimization bugfix, multiple BMP fixes
-      1.38 (2014-06-06) suppress MSVC run-time warnings, fix accidental rename of 'skip'
-      1.37 (2014-06-04) remove duplicate typedef
-      1.36 (2014-06-03) converted to header file, allow reading incorrect iphoned-images without iphone flag
-      1.35 (2014-05-27) warnings, bugfixes, TGA optimization, etc
 
    See end of file for full revision history.
 
@@ -63,7 +61,7 @@
     James "moose2000" Brown (iPhone PNG)         David Woo
     Ben "Disch" Wenger (io callbacks)            Roy Eltham
     Martin "SpartanJ" Golini                     Luke Graham
-                                                 Thomas Ruf
+    Omar Cornut (1/2/4-bit png)                  Thomas Ruf
                                                  John Bartholomew
  Optimizations & bugfixes                        Ken Hamada
     Fabian "ryg" Giesen                          Cort Stratton
@@ -588,7 +586,7 @@ static unsigned char *stbi_load_main(stbi__context *s, int *x, int *y, int *comp
 
 #ifndef STBI_NO_STDIO
 
-FILE *stbi__fopen(char const *filename, char const *mode)
+static FILE *stbi__fopen(char const *filename, char const *mode)
 {
    FILE *f;
 #if defined(_MSC_VER) && _MSC_VER >= 1400
@@ -632,7 +630,7 @@ STBIDEF unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int
    return stbi_load_main(&s,x,y,comp,req_comp);
 }
 
-unsigned char *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
+STBIDEF unsigned char *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
 {
    stbi__context s;
    stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
@@ -641,7 +639,7 @@ unsigned char *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *use
 
 #ifndef STBI_NO_HDR
 
-float *stbi_loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp)
+static float *stbi_loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp)
 {
    unsigned char *data;
    #ifndef STBI_NO_HDR
@@ -654,14 +652,14 @@ float *stbi_loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp
    return stbi__errpf("unknown image type", "Image not of any known type, or corrupt");
 }
 
-float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
 {
    stbi__context s;
    stbi__start_mem(&s,buffer,len);
    return stbi_loadf_main(&s,x,y,comp,req_comp);
 }
 
-float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
+STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
 {
    stbi__context s;
    stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
@@ -669,7 +667,7 @@ float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int
 }
 
 #ifndef STBI_NO_STDIO
-float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)
+STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)
 {
    float *result;
    FILE *f = stbi__fopen(filename, "rb");
@@ -679,7 +677,7 @@ float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)
    return result;
 }
 
-float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
 {
    stbi__context s;
    stbi__start_file(&s,f);
@@ -2040,7 +2038,7 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num)
 
    // DEFLATE spec for generating codes
    memset(sizes, 0, sizeof(sizes));
-   memset(z->fast, 255, sizeof(z->fast));
+   memset(z->fast, 0, sizeof(z->fast));
    for (i=0; i < num; ++i) 
       ++sizes[sizelist[i]];
    sizes[0] = 0;
@@ -2063,12 +2061,13 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num)
       int s = sizelist[i];
       if (s) {
          int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s];
+         stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i);
          z->size [c] = (stbi_uc     ) s;
          z->value[c] = (stbi__uint16) i;
          if (s <= STBI__ZFAST_BITS) {
             int k = stbi__bit_reverse(next_code[s],s);
             while (k < (1 << STBI__ZFAST_BITS)) {
-               z->fast[k] = (stbi__uint16) c;
+               z->fast[k] = fastv;
                k += (1 << s);
             }
          }
@@ -2123,18 +2122,9 @@ stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n)
    return k;   
 }
 
-stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
+static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z)
 {
    int b,s,k;
-   if (a->num_bits < 16) stbi__fill_bits(a);
-   b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
-   if (b < 0xffff) {
-      s = z->size[b];
-      a->code_buffer >>= s;
-      a->num_bits -= s;
-      return z->value[b];
-   }
-
    // not resolved by fast table, so compute it the slow way
    // use jpeg approach, which requires MSbits at top
    k = stbi__bit_reverse(a->code_buffer, 16);
@@ -2150,10 +2140,25 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
    return z->value[b];
 }
 
-static int stbi__zexpand(stbi__zbuf *z, int n)  // need to make room for n bytes
+stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
+{
+   int b,s;
+   if (a->num_bits < 16) stbi__fill_bits(a);
+   b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
+   if (b) {
+      s = b >> 9;
+      a->code_buffer >>= s;
+      a->num_bits -= s;
+      return b & 511;
+   }
+   return stbi__zhuffman_decode_slowpath(a, z);
+}
+
+static int stbi__zexpand(stbi__zbuf *z, char *zout, int n)  // need to make room for n bytes
 {
    char *q;
    int cur, limit;
+   z->zout = zout;
    if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG");
    cur   = (int) (z->zout     - z->zout_start);
    limit = (int) (z->zout_end - z->zout_start);
@@ -2183,16 +2188,23 @@ static int stbi__zdist_extra[32] =
 
 static int stbi__parse_huffman_block(stbi__zbuf *a)
 {
+   char *zout = a->zout;
    for(;;) {
       int z = stbi__zhuffman_decode(a, &a->z_length);
       if (z < 256) {
          if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes
-         if (a->zout >= a->zout_end) if (!stbi__zexpand(a, 1)) return 0;
-         *a->zout++ = (char) z;
+         if (zout >= a->zout_end) {
+            if (!stbi__zexpand(a, zout, 1)) return 0;
+            zout = a->zout;
+         }
+         *zout++ = (char) z;
       } else {
          stbi_uc *p;
          int len,dist;
-         if (z == 256) return 1;
+         if (z == 256) {
+            a->zout = zout;
+            return 1;
+         }
          z -= 257;
          len = stbi__zlength_base[z];
          if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
@@ -2200,11 +2212,18 @@ static int stbi__parse_huffman_block(stbi__zbuf *a)
          if (z < 0) return stbi__err("bad huffman code","Corrupt PNG");
          dist = stbi__zdist_base[z];
          if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
-         if (a->zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
-         if (a->zout + len > a->zout_end) if (!stbi__zexpand(a, len)) return 0;
-         p = (stbi_uc *) (a->zout - dist);
-         while (len--)
-            *a->zout++ = *p++;
+         if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
+         if (zout + len > a->zout_end) {
+            if (!stbi__zexpand(a, zout, len)) return 0;
+            zout = a->zout;
+         }
+         p = (stbi_uc *) (zout - dist);
+         if (dist == 1) { // run of one byte; common in images.
+            stbi_uc v = *p;
+            do *zout++ = v; while (--len);
+         } else {
+            do *zout++ = *p++; while (--len);
+         }
       }
    }
 }
@@ -2277,7 +2296,7 @@ static int stbi__parse_uncomperssed_block(stbi__zbuf *a)
    if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG");
    if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG");
    if (a->zout + len > a->zout_end)
-      if (!stbi__zexpand(a, len)) return 0;
+      if (!stbi__zexpand(a, a->zout, len)) return 0;
    memcpy(a->zout, a->zbuffer, len);
    a->zbuffer += len;
    a->zout += len;
@@ -2441,8 +2460,6 @@ typedef struct
    stbi__uint32 type;
 } stbi__pngchunk;
 
-#define PNG_TYPE(a,b,c,d)  (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
-
 static stbi__pngchunk stbi__get_chunk_header(stbi__context *s)
 {
    stbi__pngchunk c;
@@ -2468,13 +2485,23 @@ typedef struct
 
 
 enum {
-   STBI__F_none=0, STBI__F_sub=1, STBI__F_up=2, STBI__F_avg=3, STBI__F_paeth=4,
-   STBI__F_avg_first, STBI__F_paeth_first
+   STBI__F_none=0,
+   STBI__F_sub=1,
+   STBI__F_up=2,
+   STBI__F_avg=3,
+   STBI__F_paeth=4,
+   // synthetic filters used for first scanline to avoid needing a dummy row of 0s
+   STBI__F_avg_first,
+   STBI__F_paeth_first
 };
 
 static stbi_uc first_row_filter[5] =
 {
-   STBI__F_none, STBI__F_sub, STBI__F_none, STBI__F_avg_first, STBI__F_paeth_first
+   STBI__F_none,
+   STBI__F_sub,
+   STBI__F_none,
+   STBI__F_avg_first,
+   STBI__F_paeth_first
 };
 
 static int stbi__paeth(int a, int b, int c)
@@ -2490,30 +2517,50 @@ static int stbi__paeth(int a, int b, int c)
 
 #define STBI__BYTECAST(x)  ((stbi_uc) ((x) & 255))  // truncate int to byte without warnings
 
+static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
+
 // create the png data from post-deflated data
-static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y)
+static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
 {
    stbi__context *s = a->s;
    stbi__uint32 i,j,stride = x*out_n;
+   stbi__uint32 img_len, img_width_bytes;
    int k;
    int img_n = s->img_n; // copy it into a local for later
+
    STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1);
-   a->out = (stbi_uc *) stbi__malloc(x * y * out_n);
+   a->out = (stbi_uc *) stbi__malloc(x * y * out_n); // extra bytes to write off the end into
    if (!a->out) return stbi__err("outofmem", "Out of memory");
+
+   img_width_bytes = (((img_n * x * depth) + 7) >> 3);
+   img_len = (img_width_bytes + 1) * y;
    if (s->img_x == x && s->img_y == y) {
-      if (raw_len != (img_n * x + 1) * y) return stbi__err("not enough pixels","Corrupt PNG");
+      if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG");
    } else { // interlaced:
-      if (raw_len < (img_n * x + 1) * y) return stbi__err("not enough pixels","Corrupt PNG");
+      if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG");
    }
+
    for (j=0; j < y; ++j) {
       stbi_uc *cur = a->out + stride*j;
       stbi_uc *prior = cur - stride;
       int filter = *raw++;
-      if (filter > 4) return stbi__err("invalid filter","Corrupt PNG");
+      int filter_bytes = img_n;
+      int width = x;
+      if (filter > 4)
+         return stbi__err("invalid filter","Corrupt PNG");
+
+      if (depth < 8) {
+         STBI_ASSERT(img_width_bytes <= x);
+         cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place
+         filter_bytes = 1;
+         width = img_width_bytes;
+      }
+
       // if first row, use special filter that doesn't sample previous row
       if (j == 0) filter = first_row_filter[filter];
-      // handle first pixel explicitly
-      for (k=0; k < img_n; ++k) {
+
+      // handle first byte explicitly
+      for (k=0; k < filter_bytes; ++k) {
          switch (filter) {
             case STBI__F_none       : cur[k] = raw[k]; break;
             case STBI__F_sub        : cur[k] = raw[k]; break;
@@ -2524,26 +2571,37 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
             case STBI__F_paeth_first: cur[k] = raw[k]; break;
          }
       }
-      if (img_n != out_n) cur[img_n] = 255;
-      raw += img_n;
-      cur += out_n;
-      prior += out_n;
+
+      if (depth == 8) {
+         if (img_n != out_n)
+            cur[img_n] = 255; // first pixel
+         raw += img_n;
+         cur += out_n;
+         prior += out_n;
+      } else {
+         raw += 1;
+         cur += 1;
+         prior += 1;
+      }
+
       // this is a little gross, so that we don't switch per-pixel or per-component
-      if (img_n == out_n) {
+      if (depth < 8 || img_n == out_n) {
+         int nk = (width - 1)*img_n;
          #define CASE(f) \
              case f:     \
-                for (i=x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \
-                   for (k=0; k < img_n; ++k)
+                for (k=0; k < nk; ++k)
          switch (filter) {
-            CASE(STBI__F_none)         cur[k] = raw[k]; break;
-            CASE(STBI__F_sub)          cur[k] = STBI__BYTECAST(raw[k] + cur[k-img_n]); break;
+            // "none" filter turns into a memcpy here; make that explicit.
+            case STBI__F_none:         memcpy(cur, raw, nk); break;
+            CASE(STBI__F_sub)          cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break;
             CASE(STBI__F_up)           cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
-            CASE(STBI__F_avg)          cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-img_n])>>1)); break;
-            CASE(STBI__F_paeth)        cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-img_n],prior[k],prior[k-img_n])); break;
-            CASE(STBI__F_avg_first)    cur[k] = STBI__BYTECAST(raw[k] + (cur[k-img_n] >> 1)); break;
-            CASE(STBI__F_paeth_first)  cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-img_n],0,0)); break;
+            CASE(STBI__F_avg)          cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break;
+            CASE(STBI__F_paeth)        cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break;
+            CASE(STBI__F_avg_first)    cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break;
+            CASE(STBI__F_paeth_first)  cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break;
          }
          #undef CASE
+         raw += nk;
       } else {
          STBI_ASSERT(img_n+1 == out_n);
          #define CASE(f) \
@@ -2562,15 +2620,90 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
          #undef CASE
       }
    }
+
+   // we make a separate pass to expand bits to pixels; for performance,
+   // this could run two scanlines behind the above code, so it won't
+   // intefere with filtering but will still be in the cache.
+   if (depth < 8) {
+      for (j=0; j < y; ++j) {
+         stbi_uc *cur = a->out + stride*j;
+         stbi_uc *in  = a->out + stride*j + x*out_n - img_width_bytes;
+         // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit
+         // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop
+         stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range
+
+         // note that the final byte might overshoot and write more data than desired.
+         // we can allocate enough data that this never writes out of memory, but it
+         // could also overwrite the next scanline. can it overwrite non-empty data
+         // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel.
+         // so we need to explicitly clamp the final ones
+
+         if (depth == 4) {
+            for (k=x*img_n; k >= 2; k-=2, ++in) {
+               *cur++ = scale * ((*in >> 4)       );
+               *cur++ = scale * ((*in     ) & 0x0f);
+            }
+            if (k > 0) *cur++ = scale * ((*in >> 4)       );
+         } else if (depth == 2) {
+            for (k=x*img_n; k >= 4; k-=4, ++in) {
+               *cur++ = scale * ((*in >> 6)       );
+               *cur++ = scale * ((*in >> 4) & 0x03);
+               *cur++ = scale * ((*in >> 2) & 0x03);
+               *cur++ = scale * ((*in     ) & 0x03);
+            }
+            if (k > 0) *cur++ = scale * ((*in >> 6)       );
+            if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);
+            if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);
+         } else if (depth == 1) {
+            for (k=x*img_n; k >= 8; k-=8, ++in) {
+               *cur++ = scale * ((*in >> 7)       );
+               *cur++ = scale * ((*in >> 6) & 0x01);
+               *cur++ = scale * ((*in >> 5) & 0x01);
+               *cur++ = scale * ((*in >> 4) & 0x01);
+               *cur++ = scale * ((*in >> 3) & 0x01);
+               *cur++ = scale * ((*in >> 2) & 0x01);
+               *cur++ = scale * ((*in >> 1) & 0x01);
+               *cur++ = scale * ((*in     ) & 0x01);
+            }
+            if (k > 0) *cur++ = scale * ((*in >> 7)       );
+            if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);
+            if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);
+            if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);
+            if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);
+            if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);
+            if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
+         }
+         if (img_n != out_n) {
+            // insert alpha = 255
+            stbi_uc *cur = a->out + stride*j;
+            int i;
+            if (img_n == 1) {
+               for (i=x-1; i >= 0; --i) {
+                  cur[i*2+1] = 255;
+                  cur[i*2+0] = cur[i];
+               }
+            } else {
+               assert(img_n == 3);
+               for (i=x-1; i >= 0; --i) {
+                  cur[i*4+3] = 255;
+                  cur[i*4+2] = cur[i*3+2];
+                  cur[i*4+1] = cur[i*3+1];
+                  cur[i*4+0] = cur[i*3+0];
+               }
+            }
+         }
+      }
+   }
+
    return 1;
 }
 
-static int stbi__create_png_image(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, int interlaced)
+static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced)
 {
    stbi_uc *final;
    int p;
    if (!interlaced)
-      return stbi__create_png_image_raw(a, raw, raw_len, out_n, a->s->img_x, a->s->img_y);
+      return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color);
 
    // de-interlacing
    final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n);
@@ -2584,17 +2717,22 @@ static int stbi__create_png_image(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_l
       x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p];
       y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p];
       if (x && y) {
-         if (!stbi__create_png_image_raw(a, raw, raw_len, out_n, x, y)) {
+         stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y;
+         if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) {
             free(final);
             return 0;
          }
-         for (j=0; j < y; ++j)
-            for (i=0; i < x; ++i)
-               memcpy(final + (j*yspc[p]+yorig[p])*a->s->img_x*out_n + (i*xspc[p]+xorig[p])*out_n,
+         for (j=0; j < y; ++j) {
+            for (i=0; i < x; ++i) {
+               int out_y = j*yspc[p]+yorig[p];
+               int out_x = i*xspc[p]+xorig[p];
+               memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n,
                       a->out + (j*x+i)*out_n, out_n);
+            }
+         }
          free(a->out);
-         raw += (x*out_n+1)*y;
-         raw_len -= (x*out_n+1)*y;
+         image_data += img_len;
+         image_data_len -= img_len;
       }
    }
    a->out = final;
@@ -2719,12 +2857,14 @@ static void stbi__de_iphone(stbi__png *z)
    }
 }
 
+#define STBI__PNG_TYPE(a,b,c,d)  (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
+
 static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
 {
    stbi_uc palette[1024], pal_img_n=0;
    stbi_uc has_trans=0, tc[3];
    stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0;
-   int first=1,k,interlace=0, is_iphone=0;
+   int first=1,k,interlace=0, color=0, depth=0, is_iphone=0;
    stbi__context *s = z->s;
 
    z->expanded = NULL;
@@ -2738,18 +2878,18 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
    for (;;) {
       stbi__pngchunk c = stbi__get_chunk_header(s);
       switch (c.type) {
-         case PNG_TYPE('C','g','B','I'):
+         case STBI__PNG_TYPE('C','g','B','I'):
             is_iphone = 1;
             stbi__skip(s, c.length);
             break;
-         case PNG_TYPE('I','H','D','R'): {
-            int depth,color,comp,filter;
+         case STBI__PNG_TYPE('I','H','D','R'): {
+            int comp,filter;
             if (!first) return stbi__err("multiple IHDR","Corrupt PNG");
             first = 0;
             if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG");
             s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)");
             s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)");
-            depth = stbi__get8(s);  if (depth != 8)        return stbi__err("8bit only","PNG not supported: 8-bit only");
+            depth = stbi__get8(s);  if (depth != 1 && depth != 2 && depth != 4 && depth != 8)  return stbi__err("1/2/4/8-bit only","PNG not supported: 1/2/4/8-bit only");
             color = stbi__get8(s);  if (color > 6)         return stbi__err("bad ctype","Corrupt PNG");
             if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG");
             comp  = stbi__get8(s);  if (comp) return stbi__err("bad comp method","Corrupt PNG");
@@ -2770,7 +2910,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
             break;
          }
 
-         case PNG_TYPE('P','L','T','E'):  {
+         case STBI__PNG_TYPE('P','L','T','E'):  {
             if (first) return stbi__err("first not IHDR", "Corrupt PNG");
             if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG");
             pal_len = c.length / 3;
@@ -2784,7 +2924,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
             break;
          }
 
-         case PNG_TYPE('t','R','N','S'): {
+         case STBI__PNG_TYPE('t','R','N','S'): {
             if (first) return stbi__err("first not IHDR", "Corrupt PNG");
             if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG");
             if (pal_img_n) {
@@ -2799,12 +2939,12 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
                if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
                has_trans = 1;
                for (k=0; k < s->img_n; ++k)
-                  tc[k] = (stbi_uc) (stbi__get16be(s) & 255); // non 8-bit images will be larger
+                  tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[depth]; // non 8-bit images will be larger
             }
             break;
          }
 
-         case PNG_TYPE('I','D','A','T'): {
+         case STBI__PNG_TYPE('I','D','A','T'): {
             if (first) return stbi__err("first not IHDR", "Corrupt PNG");
             if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
             if (scan == SCAN_header) { s->img_n = pal_img_n; return 1; }
@@ -2821,19 +2961,21 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
             break;
          }
 
-         case PNG_TYPE('I','E','N','D'): {
+         case STBI__PNG_TYPE('I','E','N','D'): {
             stbi__uint32 raw_len;
             if (first) return stbi__err("first not IHDR", "Corrupt PNG");
             if (scan != SCAN_load) return 1;
             if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG");
-            z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, 16384, (int *) &raw_len, !is_iphone);
+            // initial guess for decoded data size to avoid unnecessary reallocs
+            raw_len = s->img_x * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */;
+            z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone);
             if (z->expanded == NULL) return 0; // zlib should set error
             free(z->idata); z->idata = NULL;
             if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans)
                s->img_out_n = s->img_n+1;
             else
                s->img_out_n = s->img_n;
-            if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, interlace)) return 0;
+            if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, depth, color, interlace)) return 0;
             if (has_trans)
                if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0;
             if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2)
@@ -4685,12 +4827,16 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int
 
 /*
    revision history:
+      1.48 (2014-12-14) fix incorrectly-named assert()
+      1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb)
+                        optimize PNG (ryg)
+                        fix bug in interlaced PNG with user-specified channel count (stb)
       1.46 (2014-08-26)
              fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG
       1.45 (2014-08-16)
              fix MSVC-ARM internal compiler error by wrapping malloc
       1.44 (2014-08-07)
-		       various warning fixes from Ronny Chevalier
+               various warning fixes from Ronny Chevalier
       1.43 (2014-07-15)
              fix MSVC-only compiler problem in code changed in 1.42
       1.42 (2014-07-09)

+ 40 - 22
stb_rect_pack.h

@@ -1,4 +1,4 @@
-// stb_rect_pack.h - v0.02 - public domain - rectangle packing
+// stb_rect_pack.h - v0.05 - public domain - rectangle packing
 // Sean Barrett 2014
 //
 // Useful for e.g. packing rectangular textures into an atlas.
@@ -19,7 +19,12 @@
 // Please note: better rectangle packers are welcome! Please
 // implement them to the same API, but with a different init
 // function.
-
+//
+// Version history:
+//
+//     0.05:  added STBRP_ASSERT to allow replacing assert
+//     0.04:  fixed minor bug in STBRP_LARGE_RECTS support
+//     0.01:  initial release
 
 //////////////////////////////////////////////////////////////////////////////
 //
@@ -29,6 +34,8 @@
 #ifndef STB_INCLUDE_STB_RECT_PACK_H
 #define STB_INCLUDE_STB_RECT_PACK_H
 
+#define STB_RECT_PACK_VERSION  1
+
 #ifdef STBRP_STATIC
 #define STBRP_DEF static
 #else
@@ -164,6 +171,11 @@ struct stbrp_context
 #ifdef STB_RECT_PACK_IMPLEMENTATION
 #include <stdlib.h>
 
+#ifndef STBRP_ASSERT
+#include <assert.h>
+#define STBRP_ASSERT assert
+#endif
+
 enum
 {
    STBRP__INIT_skyline = 1,
@@ -173,11 +185,11 @@ STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
 {
    switch (context->init_mode) {
       case STBRP__INIT_skyline:
-         assert(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
+         STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
          context->heuristic = heuristic;
          break;
       default:
-         assert(0);
+         STBRP_ASSERT(0);
    }
 }
 
@@ -205,7 +217,7 @@ STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height,
 {
    int i;
 #ifndef STBRP_LARGE_RECTS
-   assert(width <= 0xffff && height <= 0xffff);
+   STBRP_ASSERT(width <= 0xffff && height <= 0xffff);
 #endif
 
    for (i=0; i < num_nodes-1; ++i)
@@ -224,7 +236,7 @@ STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height,
    context->extra[0].x = 0;
    context->extra[0].y = 0;
    context->extra[0].next = &context->extra[1];
-   context->extra[1].x = width;
+   context->extra[1].x = (stbrp_coord) width;
 #ifdef STBRP_LARGE_RECTS
    context->extra[1].y = (1<<30);
 #else
@@ -239,17 +251,17 @@ static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0
    stbrp_node *node = first;
    int x1 = x0 + width;
    int min_y, visited_width, waste_area;
-   assert(first->x <= x0);
+   STBRP_ASSERT(first->x <= x0);
 
    #if 0
    // skip in case we're past the node
    while (node->next->x <= x0)
       ++node;
    #else
-   assert(node->next->x > x0); // we ended up handling this in the caller for efficiency
+   STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
    #endif
 
-   assert(node->x <= x0);
+   STBRP_ASSERT(node->x <= x0);
 
    min_y = 0;
    waste_area = 0;
@@ -296,7 +308,7 @@ static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int widt
    // align to multiple of c->align
    width = (width + c->align - 1);
    width -= width % c->align;
-   assert(width % c->align == 0);
+   STBRP_ASSERT(width % c->align == 0);
 
    node = c->active_head;
    prev = &c->active_head;
@@ -353,19 +365,19 @@ static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int widt
       while (tail) {
          int xpos = tail->x - width;
          int y,waste;
-         assert(xpos >= 0);
+         STBRP_ASSERT(xpos >= 0);
          // find the left position that matches this
          while (node->next->x <= xpos) {
             prev = &node->next;
             node = node->next;
          }
-         assert(node->next->x > xpos && node->x <= xpos);
+         STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
          y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
          if (y + height < c->height) {
             if (y <= best_y) {
                if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
                   best_x = xpos;
-                  assert(y <= best_y);
+                  STBRP_ASSERT(y <= best_y);
                   best_y = y;
                   best_waste = waste;
                   best = prev;
@@ -399,8 +411,8 @@ static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, i
 
    // on success, create new node
    node = context->free_head;
-   node->x = res.x;
-   node->y = res.y + height;
+   node->x = (stbrp_coord) res.x;
+   node->y = (stbrp_coord) (res.y + height);
 
    context->free_head = node->next;
 
@@ -432,15 +444,15 @@ static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, i
    node->next = cur;
 
    if (cur->x < res.x + width)
-      cur->x = res.x+width;
+      cur->x = (stbrp_coord) (res.x + width);
 
 #ifdef _DEBUG
    cur = context->active_head;
    while (cur->x < context->width) {
-      assert(cur->x < cur->next->x);
+      STBRP_ASSERT(cur->x < cur->next->x);
       cur = cur->next;
    }
-   assert(cur->next == NULL);
+   STBRP_ASSERT(cur->next == NULL);
 
    {
       stbrp_node *L1 = NULL, *L2 = NULL;
@@ -457,7 +469,7 @@ static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, i
          cur = cur->next;
          ++count;
       }
-      assert(count == context->num_nodes+2);
+      STBRP_ASSERT(count == context->num_nodes+2);
    }
 #endif
 
@@ -493,6 +505,12 @@ static int rect_original_order(const void *a, const void *b)
    return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
 }
 
+#ifdef STBRP_LARGE_RECTS
+#define STBRP__MAXVAL  0xffffffff
+#else
+#define STBRP__MAXVAL  0xffff
+#endif
+
 STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
 {
    int i;
@@ -501,7 +519,7 @@ STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int n
    for (i=0; i < num_rects; ++i) {
       rects[i].was_packed = i;
       #ifndef STBRP_LARGE_RECTS
-      assert(rects[i].w <= 0xffff && rects[i].h <= 0xffff);
+      STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff);
       #endif
    }
 
@@ -514,7 +532,7 @@ STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int n
          rects[i].x = (stbrp_coord) fr.x;
          rects[i].y = (stbrp_coord) fr.y;
       } else {
-         rects[i].x = rects[i].y = 0xffff;
+         rects[i].x = rects[i].y = STBRP__MAXVAL;
       }
    }
 
@@ -523,6 +541,6 @@ STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int n
 
    // set was_packed flags
    for (i=0; i < num_rects; ++i)
-      rects[i].was_packed = !(rects[i].x == 0xffff && rects[i].y == 0xffff);
+      rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
 }
 #endif

+ 8 - 8
stb_tilemap_editor.h

@@ -1971,14 +1971,14 @@ static int stbte__float_control(int x0, int y0, int w, float minv, float maxv, f
             stbte__ui.accum_y -= ay*STBTE_FLOAT_CONTROL_GRANULARITY;
             if (stbte__ui.shift) {
                if (stbte__ui.active_event == STBTE__leftdown)
-                  delta = ax * 16 + ay;
+                  delta = ax * 16.0f + ay;
                else
-                  delta = ax / 16.0 + ay / 256.0;
+                  delta = ax / 16.0f + ay / 256.0f;
             } else {
                if (stbte__ui.active_event == STBTE__leftdown)
-                  delta = ax*10 + ay;
+                  delta = ax*10.0f + ay;
                else
-                  delta = ax * 0.1 + ay * 0.01;
+                  delta = ax * 0.1f + ay * 0.01f;
             }
             v += delta * scale;
             if (v < minv) v = minv;
@@ -3553,8 +3553,8 @@ static void stbte__props_panel(stbte_tilemap *tm, int x0, int y0, int w, int h)
                int flag = (int) p[i];
                if (stbte__layerbutton(x,y, flag ? 'x' : ' ', STBTE__ID(STBTE__prop_flag,i), flag, 0, 2)) {
                   stbte__begin_undo(tm);
-                  stbte__undo_record_prop_float(tm,mx,my,i,flag);
-                  p[i] = !flag;
+                  stbte__undo_record_prop_float(tm,mx,my,i,(float) flag);
+                  p[i] = (float) !flag;
                   stbte__end_undo(tm);
                }
                stbte__draw_text(x+13,y+1,s,x1-(x+13)-2,STBTE__TEXTCOLOR(STBTE__cpanel));
@@ -3568,7 +3568,7 @@ static void stbte__props_panel(stbte_tilemap *tm, int x0, int y0, int w, int h)
                if (a+v != p[i] || v < 0 || v > b-a) {
                   if (v < 0) v = 0;
                   if (v > b-a) v = b-a;
-                  p[i] = a+v; // @TODO undo
+                  p[i] = (float) (a+v); // @TODO undo
                }
                switch (stbte__slider(x, slider_width, y+7, b-a, &v, STBTE__ID(STBTE__prop_int,i)))
                {
@@ -3576,7 +3576,7 @@ static void stbte__props_panel(stbte_tilemap *tm, int x0, int y0, int w, int h)
                      stbte__saved = p[i];
                      // fallthrough
                   case STBTE__change:
-                     p[i] = a+v; // @TODO undo
+                     p[i] = (float) (a+v); // @TODO undo
                      break;
                   case STBTE__end:
                      if (p[i] != stbte__saved) {

+ 522 - 9
stb_truetype.h

@@ -1,4 +1,4 @@
-// stb_truetype.h - v0.99 - public domain
+// stb_truetype.h - v1.02 - public domain
 // authored from 2009-2014 by Sean Barrett / RAD Game Tools
 //
 //   This library processes TrueType files:
@@ -35,8 +35,15 @@
 //       Hou Qiming
 //       Fabian "ryg" Giesen
 //
+//   Misc other:
+//       Ryan Gordon
+//
 // VERSION HISTORY
 //
+//   1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++
+//   1.01 (2014-12-08) fix subpixel position when oversampling to exactly match
+//                        non-oversampled; STBTT_POINT_SIZE for packed case only
+//   1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling
 //   0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg)
 //   0.9  (2014-08-07) support certain mac/iOS fonts without an MS platformID
 //   0.8b (2014-07-07) fix a warning
@@ -58,7 +65,7 @@
 //                    updated Hello World! sample to use kerning and subpixel
 //                    fixed some warnings
 //   0.3  (2009-06-24) cmap fmt=12, compound shapes (MM)
-//                    userdata, malloc-from-userdata, non-zero fill (STB)
+//                    userdata, malloc-from-userdata, non-zero fill (stb)
 //   0.2  (2009-03-11) Fix unsigned/signed char warnings
 //   0.1  (2009-03-09) First public release
 //
@@ -76,11 +83,18 @@
 //   before the #include of this file. This expands out the actual
 //   implementation into that C/C++ file.
 //
-//   Simple 3D API (don't ship this, but it's fine for tools and quick start,
-//                  and you can cut and paste from it to move to more advanced)
+//   Simple 3D API (don't ship this, but it's fine for tools and quick start)
 //           stbtt_BakeFontBitmap()               -- bake a font to a bitmap for use as texture
 //           stbtt_GetBakedQuad()                 -- compute quad to draw for a given char
 //
+//   Improved 3D API (more shippable):
+//           #include "stb_rect_pack.h"           -- optional, but you really want it
+//           stbtt_PackBegin()
+//           stbtt_PackSetOversample()            -- for improved quality on small fonts
+//           stbtt_PackFontRanges()
+//           stbtt_PackEnd()
+//           stbtt_GetPackedQuad()
+//
 //   "Load" a font file from a memory buffer (you have to keep the buffer loaded)
 //           stbtt_InitFont()
 //           stbtt_GetFontOffsetForIndex()        -- use for TTC font collections
@@ -428,7 +442,7 @@ extern "C" {
 typedef struct
 {
    unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
-   float xoff,yoff,xadvance;   
+   float xoff,yoff,xadvance;
 } stbtt_bakedchar;
 
 extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset,  // font location (use offset=0 for plain .ttf)
@@ -463,6 +477,100 @@ extern void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph,  // sa
 // It's inefficient; you might want to c&p it and optimize it.
 
 
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// NEW TEXTURE BAKING API
+//
+// This provides options for packing multiple fonts into one atlas, not
+// perfectly but better than nothing.
+
+typedef struct
+{
+   unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
+   float xoff,yoff,xadvance;
+   float xoff2,yoff2;
+} stbtt_packedchar;
+
+typedef struct stbtt_pack_context stbtt_pack_context;
+
+extern int  stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context);
+// Initializes a packing context stored in the passed-in stbtt_pack_context.
+// Future calls using this context will pack characters into the bitmap passed
+// in here: a 1-channel bitmap that is weight x height. stride_in_bytes is
+// the distance from one row to the next (or 0 to mean they are packed tightly
+// together). "padding" is // the amount of padding to leave between each
+// character (normally you want '1' for bitmaps you'll use as textures with
+// bilinear filtering).
+//
+// Returns 0 on failure, 1 on success.
+
+extern void stbtt_PackEnd  (stbtt_pack_context *spc);
+// Cleans up the packing context and frees all memory.
+
+#define STBTT_POINT_SIZE(x)   (-(x))
+
+extern int  stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,
+                                int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range);
+// Creates character bitmaps from the font_index'th font found in fontdata (use
+// font_index=0 if you don't know what that is). It creates num_chars_in_range
+// bitmaps for characters with unicode values starting at first_unicode_char_in_range
+// and increasing. Data for how to render them is stored in chardata_for_range;
+// pass these to stbtt_GetPackedQuad to get back renderable quads.
+//
+// font_size is the full height of the character from ascender to descender,
+// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed
+// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE()
+// and pass that result as 'font_size':
+//       ...,                  20 , ... // font max minus min y is 20 pixels tall
+//       ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall
+
+typedef struct
+{
+   float font_size;
+   int first_unicode_char_in_range;
+   int num_chars_in_range;
+   stbtt_packedchar *chardata_for_range; // output
+} stbtt_pack_range;
+
+extern int  stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges);
+// Creates character bitmaps from multiple ranges of characters stored in
+// ranges. This will usually create a better-packed bitmap than multiple
+// calls to stbtt_PackFontRange.
+
+
+extern void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample);
+// Oversampling a font increases the quality by allowing higher-quality subpixel
+// positioning, and is especially valuable at smaller text sizes.
+//
+// This function sets the amount of oversampling for all following calls to
+// stbtt_PackFontRange(s). The default (no oversampling) is achieved by
+// h_oversample=1, v_oversample=1. The total number of pixels required is
+// h_oversample*v_oversample larger than the default; for example, 2x2
+// oversampling requires 4x the storage of 1x1. For best results, render
+// oversampled textures with bilinear filtering. Look at the readme in
+// stb/tests/oversample for information about oversampled fonts
+
+extern void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph,  // same data as above
+                               int char_index,             // character to display
+                               float *xpos, float *ypos,   // pointers to current position in screen pixel space
+                               stbtt_aligned_quad *q,      // output: quad to draw
+                               int align_to_integer);
+
+// this is an opaque structure that you shouldn't mess with which holds
+// all the context needed from PackBegin to PackEnd.
+struct stbtt_pack_context {
+   void *user_allocator_context;
+   void *pack_info;
+   int   width;
+   int   height;
+   int   stride_in_bytes;
+   int   padding;
+   unsigned int   h_oversample, v_oversample;
+   unsigned char *pixels;
+   void  *nodes;
+};
+
 //////////////////////////////////////////////////////////////////////////////
 //
 // FONT LOADING
@@ -770,6 +878,12 @@ enum { // languageID for STBTT_PLATFORM_ID_MAC
 
 #ifdef STB_TRUETYPE_IMPLEMENTATION
 
+#ifndef STBTT_MAX_OVERSAMPLE
+#define STBTT_MAX_OVERSAMPLE   8
+#endif
+
+typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1];
+
 //////////////////////////////////////////////////////////////////////////
 //
 // accessors to parse data from file
@@ -1880,7 +1994,8 @@ extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset,  // font
    float scale;
    int x,y,bottom_y, i;
    stbtt_fontinfo f;
-   stbtt_InitFont(&f, data, offset);
+   if (!stbtt_InitFont(&f, data, offset))
+      return -1;
    STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
    x=y=1;
    bottom_y = 1;
@@ -1908,9 +2023,9 @@ extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset,  // font
       chardata[i].xadvance = scale * advance;
       chardata[i].xoff     = (float) x0;
       chardata[i].yoff     = (float) y0;
-      x = x + gw + 2;
-      if (y+gh+2 > bottom_y)
-         bottom_y = y+gh+2;
+      x = x + gw + 1;
+      if (y+gh+1 > bottom_y)
+         bottom_y = y+gh+1;
    }
    return bottom_y;
 }
@@ -1936,6 +2051,404 @@ void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_inde
    *xpos += b->xadvance;
 }
 
+//////////////////////////////////////////////////////////////////////////////
+//
+// rectangle packing replacement routines if you don't have stb_rect_pack.h
+//
+
+#ifndef STB_RECT_PACK_VERSION
+#ifdef _MSC_VER
+#define STBTT__NOTUSED(v)  (void)(v)
+#else
+#define STBTT__NOTUSED(v)  (void)sizeof(v)
+#endif
+
+typedef int stbrp_coord;
+
+////////////////////////////////////////////////////////////////////////////////////
+//                                                                                //
+//                                                                                //
+// COMPILER WARNING ?!?!?                                                         //
+//                                                                                //
+//                                                                                //
+// if you get a compile warning due to these symbols being defined more than      //
+// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h"         //
+//                                                                                //
+////////////////////////////////////////////////////////////////////////////////////
+
+typedef struct
+{
+   int width,height;
+   int x,y,bottom_y;
+} stbrp_context;
+
+typedef struct
+{
+   unsigned char x;
+} stbrp_node;
+
+typedef struct
+{
+   stbrp_coord x,y;
+   int id,w,h,was_packed;
+} stbrp_rect;
+
+static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes)
+{
+   con->width  = pw;
+   con->height = ph;
+   con->x = 0;
+   con->y = 0;
+   con->bottom_y = 0;
+   STBTT__NOTUSED(nodes);
+   STBTT__NOTUSED(num_nodes);   
+}
+
+static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects)
+{
+   int i;
+   for (i=0; i < num_rects; ++i) {
+      if (con->x + rects[i].w > con->width) {
+         con->x = 0;
+         con->y = con->bottom_y;
+      }
+      if (con->y + rects[i].h > con->height)
+         break;
+      rects[i].x = con->x;
+      rects[i].y = con->y;
+      rects[i].was_packed = 1;
+      con->x += rects[i].w;
+      if (con->y + rects[i].h > con->bottom_y)
+         con->bottom_y = con->y + rects[i].h;
+   }
+   for (   ; i < num_rects; ++i)
+      rects[i].was_packed = 0;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// bitmap baking
+//
+// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If
+// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy.
+
+int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context)
+{
+   stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context)            ,alloc_context);
+   int            num_nodes = pw - padding;
+   stbrp_node    *nodes   = (stbrp_node    *) STBTT_malloc(sizeof(*nodes  ) * num_nodes,alloc_context);
+
+   if (context == NULL || nodes == NULL) {
+      if (context != NULL) STBTT_free(context, alloc_context);
+      if (nodes   != NULL) STBTT_free(nodes  , alloc_context);
+      return 0;
+   }
+
+   spc->user_allocator_context = alloc_context;
+   spc->width = pw;
+   spc->height = ph;
+   spc->pixels = pixels;
+   spc->pack_info = context;
+   spc->nodes = nodes;
+   spc->padding = padding;
+   spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw;
+   spc->h_oversample = 1;
+   spc->v_oversample = 1;
+
+   stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes);
+
+   STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
+
+   return 1;
+}
+
+void stbtt_PackEnd  (stbtt_pack_context *spc)
+{
+   STBTT_free(spc->nodes    , spc->user_allocator_context);
+   STBTT_free(spc->pack_info, spc->user_allocator_context);
+}
+
+void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample)
+{
+   STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE);
+   STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE);
+   if (h_oversample <= STBTT_MAX_OVERSAMPLE)
+      spc->h_oversample = h_oversample;
+   if (v_oversample <= STBTT_MAX_OVERSAMPLE)
+      spc->v_oversample = v_oversample;
+}
+
+#define STBTT__OVER_MASK  (STBTT_MAX_OVERSAMPLE-1)
+
+static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
+{
+   unsigned char buffer[STBTT_MAX_OVERSAMPLE];
+   int safe_w = w - kernel_width;
+   int j;
+   for (j=0; j < h; ++j) {
+      int i;
+      unsigned int total;
+      memset(buffer, 0, kernel_width);
+
+      total = 0;
+
+      // make kernel_width a constant in common cases so compiler can optimize out the divide
+      switch (kernel_width) {
+         case 2:
+            for (i=0; i <= safe_w; ++i) {
+               total += pixels[i] - buffer[i & STBTT__OVER_MASK];
+               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
+               pixels[i] = (unsigned char) (total / 2);
+            }
+            break;
+         case 3:
+            for (i=0; i <= safe_w; ++i) {
+               total += pixels[i] - buffer[i & STBTT__OVER_MASK];
+               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
+               pixels[i] = (unsigned char) (total / 3);
+            }
+            break;
+         case 4:
+            for (i=0; i <= safe_w; ++i) {
+               total += pixels[i] - buffer[i & STBTT__OVER_MASK];
+               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
+               pixels[i] = (unsigned char) (total / 4);
+            }
+            break;
+         default:
+            for (i=0; i <= safe_w; ++i) {
+               total += pixels[i] - buffer[i & STBTT__OVER_MASK];
+               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
+               pixels[i] = (unsigned char) (total / kernel_width);
+            }
+            break;
+      }
+
+      for (; i < w; ++i) {
+         STBTT_assert(pixels[i] == 0);
+         total -= buffer[i & STBTT__OVER_MASK];
+         pixels[i] = (unsigned char) (total / kernel_width);
+      }
+
+      pixels += stride_in_bytes;
+   }
+}
+
+static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
+{
+   unsigned char buffer[STBTT_MAX_OVERSAMPLE];
+   int safe_h = h - kernel_width;
+   int j;
+   for (j=0; j < w; ++j) {
+      int i;
+      unsigned int total;
+      memset(buffer, 0, kernel_width);
+
+      total = 0;
+
+      // make kernel_width a constant in common cases so compiler can optimize out the divide
+      switch (kernel_width) {
+         case 2:
+            for (i=0; i <= safe_h; ++i) {
+               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
+               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
+               pixels[i*stride_in_bytes] = (unsigned char) (total / 2);
+            }
+            break;
+         case 3:
+            for (i=0; i <= safe_h; ++i) {
+               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
+               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
+               pixels[i*stride_in_bytes] = (unsigned char) (total / 3);
+            }
+            break;
+         case 4:
+            for (i=0; i <= safe_h; ++i) {
+               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
+               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
+               pixels[i*stride_in_bytes] = (unsigned char) (total / 4);
+            }
+            break;
+         default:
+            for (i=0; i <= safe_h; ++i) {
+               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
+               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
+               pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);
+            }
+            break;
+      }
+
+      for (; i < h; ++i) {
+         STBTT_assert(pixels[i*stride_in_bytes] == 0);
+         total -= buffer[i & STBTT__OVER_MASK];
+         pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);
+      }
+
+      pixels += 1;
+   }
+}
+
+static float stbtt__oversample_shift(int oversample)
+{
+   if (!oversample)
+      return 0.0f;
+
+   // The prefilter is a box filter of width "oversample",
+   // which shifts phase by (oversample - 1)/2 pixels in
+   // oversampled space. We want to shift in the opposite
+   // direction to counter this.
+   return (float)-(oversample - 1) / (2.0f * (float)oversample);
+}
+
+int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)
+{
+   stbtt_fontinfo info;
+   float recip_h = 1.0f / spc->h_oversample;
+   float recip_v = 1.0f / spc->v_oversample;
+   float sub_x = stbtt__oversample_shift(spc->h_oversample);
+   float sub_y = stbtt__oversample_shift(spc->v_oversample);
+   int i,j,k,n, return_value = 1;
+   stbrp_context *context = (stbrp_context *) spc->pack_info;
+   stbrp_rect    *rects;
+
+   // flag all characters as NOT packed
+   for (i=0; i < num_ranges; ++i)
+      for (j=0; j < ranges[i].num_chars_in_range; ++j)
+         ranges[i].chardata_for_range[j].x0 =
+         ranges[i].chardata_for_range[j].y0 =
+         ranges[i].chardata_for_range[j].x1 =
+         ranges[i].chardata_for_range[j].y1 = 0;
+
+   n = 0;
+   for (i=0; i < num_ranges; ++i)
+      n += ranges[i].num_chars_in_range;
+         
+   rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context);
+   if (rects == NULL)
+      return 0;
+
+   stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index));
+   k=0;
+   for (i=0; i < num_ranges; ++i) {
+      float fh = ranges[i].font_size;
+      float scale = fh > 0 ? stbtt_ScaleForPixelHeight(&info, fh) : stbtt_ScaleForMappingEmToPixels(&info, -fh);
+      for (j=0; j < ranges[i].num_chars_in_range; ++j) {
+         int x0,y0,x1,y1;
+         stbtt_GetCodepointBitmapBoxSubpixel(&info, ranges[i].first_unicode_char_in_range + j,
+                                              scale * spc->h_oversample,
+                                              scale * spc->v_oversample,
+                                              0,0,
+                                              &x0,&y0,&x1,&y1);
+         rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);
+         rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);
+         ++k;
+      }
+   }
+
+   stbrp_pack_rects(context, rects, k);
+
+   k = 0;
+   for (i=0; i < num_ranges; ++i) {
+      float fh = ranges[i].font_size;
+      float scale = fh > 0 ? stbtt_ScaleForPixelHeight(&info, fh) : stbtt_ScaleForMappingEmToPixels(&info, -fh);
+      for (j=0; j < ranges[i].num_chars_in_range; ++j) {
+         stbrp_rect *r = &rects[k];
+         if (r->was_packed) {
+            stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];
+            int advance, lsb, x0,y0,x1,y1;
+            int glyph = stbtt_FindGlyphIndex(&info, ranges[i].first_unicode_char_in_range + j);
+            stbrp_coord pad = (stbrp_coord) spc->padding;
+
+            // pad on left and top
+            r->x += pad;
+            r->y += pad;
+            r->w -= pad;
+            r->h -= pad;
+            stbtt_GetGlyphHMetrics(&info, glyph, &advance, &lsb);
+            stbtt_GetGlyphBitmapBox(&info, glyph,
+                                    scale * spc->h_oversample,
+                                    scale * spc->v_oversample,
+                                    &x0,&y0,&x1,&y1);
+            stbtt_MakeGlyphBitmapSubpixel(&info,
+                                          spc->pixels + r->x + r->y*spc->stride_in_bytes,
+                                          r->w - spc->h_oversample+1,
+                                          r->h - spc->v_oversample+1,
+                                          spc->stride_in_bytes,
+                                          scale * spc->h_oversample,
+                                          scale * spc->v_oversample,
+                                          0,0,
+                                          glyph);
+
+            if (spc->h_oversample > 1)
+               stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
+                                  r->w, r->h, spc->stride_in_bytes,
+                                  spc->h_oversample);
+
+            if (spc->v_oversample > 1)
+               stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
+                                  r->w, r->h, spc->stride_in_bytes,
+                                  spc->v_oversample);
+
+            bc->x0       = (stbtt_int16)  r->x;
+            bc->y0       = (stbtt_int16)  r->y;
+            bc->x1       = (stbtt_int16) (r->x + r->w);
+            bc->y1       = (stbtt_int16) (r->y + r->h);
+            bc->xadvance =                scale * advance;
+            bc->xoff     =       (float)  x0 * recip_h + sub_x;
+            bc->yoff     =       (float)  y0 * recip_v + sub_y;
+            bc->xoff2    =                (x0 + r->w) * recip_h + sub_x;
+            bc->yoff2    =                (y0 + r->h) * recip_v + sub_y;
+         } else {
+            return_value = 0; // if any fail, report failure
+         }
+
+         ++k;
+      }
+   }
+
+   return return_value;
+}
+
+int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,
+            int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range)
+{
+   stbtt_pack_range range;
+   range.first_unicode_char_in_range = first_unicode_char_in_range;
+   range.num_chars_in_range          = num_chars_in_range;
+   range.chardata_for_range          = chardata_for_range;
+   range.font_size                   = font_size;
+   return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1);
+}
+
+void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)
+{
+   float ipw = 1.0f / pw, iph = 1.0f / ph;
+   stbtt_packedchar *b = chardata + char_index;
+
+   if (align_to_integer) {
+      float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5);
+      float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5);
+      q->x0 = x;
+      q->y0 = y;
+      q->x1 = x + b->xoff2 - b->xoff;
+      q->y1 = y + b->yoff2 - b->yoff;
+   } else {
+      q->x0 = *xpos + b->xoff;
+      q->y0 = *ypos + b->yoff;
+      q->x1 = *xpos + b->xoff2;
+      q->y1 = *ypos + b->yoff2;
+   }
+
+   q->s0 = b->x0 * ipw;
+   q->t0 = b->y0 * iph;
+   q->s1 = b->x1 * ipw;
+   q->t1 = b->y1 * iph;
+
+   *xpos += b->xadvance;
+}
+
+
 //////////////////////////////////////////////////////////////////////////////
 //
 // font name matching -- recommended not to use this

+ 2 - 1
tests/c_lexer_test.dsp

@@ -64,7 +64,8 @@ LINK32=link.exe
 # PROP Intermediate_Dir "Debug\c_lexer_test"
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
-# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c
+# SUBTRACT CPP /YX
 # ADD BASE RSC /l 0x409 /d "_DEBUG"
 # ADD RSC /l 0x409 /d "_DEBUG"
 BSC32=bscmake.exe

+ 46 - 9
tests/image_test.c

@@ -7,6 +7,8 @@
 #define STB_DEFINE
 #include "stb.h"
 
+#define PNGSUITE_PRIMARY
+
 int main(int argc, char **argv)
 {
    int w,h;
@@ -31,25 +33,60 @@ int main(int argc, char **argv)
             printf("FAILED 4\n");
       }
    } else {
-      int i;
+      int i, nope=0;
+      #ifdef PNGSUITE_PRIMARY
+      char **files = stb_readdir_files("pngsuite/primary");
+      #else
       char **files = stb_readdir_files("images");
+      #endif
       for (i=0; i < stb_arr_len(files); ++i) {
          int n;
+         char **failed = NULL;
          unsigned char *data;
-         printf("%s\n", files[i]);
-         data = stbi_load(files[i], &w, &h, &n, 4); if (data) free(data); else printf("Failed &n\n");
-         data = stbi_load(files[i], &w, &h,  0, 1); if (data) free(data); else printf("Failed 1\n");
-         data = stbi_load(files[i], &w, &h,  0, 2); if (data) free(data); else printf("Failed 2\n");
-         data = stbi_load(files[i], &w, &h,  0, 3); if (data) free(data); else printf("Failed 3\n");
-         data = stbi_load(files[i], &w, &h,  0, 4);
+         printf(".");
+         //printf("%s\n", files[i]);
+         data = stbi_load(files[i], &w, &h, &n, 0); if (data) free(data); else stb_arr_push(failed, "&n");
+         data = stbi_load(files[i], &w, &h,  0, 1); if (data) free(data); else stb_arr_push(failed, "1");
+         data = stbi_load(files[i], &w, &h,  0, 2); if (data) free(data); else stb_arr_push(failed, "2");
+         data = stbi_load(files[i], &w, &h,  0, 3); if (data) free(data); else stb_arr_push(failed, "3");
+         data = stbi_load(files[i], &w, &h,  0, 4); if (data)           ; else stb_arr_push(failed, "4");
          if (data) {
             char fname[512];
+
+            #ifdef PNGSUITE_PRIMARY
+            int w2,h2;
+            unsigned char *data2;
+            stb_splitpath(fname, files[i], STB_FILE_EXT);
+            data2 = stbi_load(stb_sprintf("pngsuite/primary_check/%s", fname), &w2, &h2, 0, 4);
+            if (!data2)
+               printf("FAILED: couldn't load 'pngsuite/primary_check/%s\n", fname);
+            else {
+               if (w != w2 || h != w2 || 0 != memcmp(data, data2, w*h*4)) {
+                  int x,y,c;
+                  if (w == w2 && h == h2)
+                     for (y=0; y < h; ++y)
+                        for (x=0; x < w; ++x)
+                           for (c=0; c < 4; ++c)
+                              assert(data[y*w*4+x*4+c] == data2[y*w*4+x*4+c]);
+                  printf("FAILED: %s loaded but didn't match PRIMARY_check 32-bit version\n", files[i]);
+               }
+               free(data2);
+            }
+            #else
             stb_splitpath(fname, files[i], STB_FILE);
             stbi_write_png(stb_sprintf("output/%s.png", fname), w, h, 4, data, w*4);
+            #endif
             free(data);
-         } else
-            printf("FAILED\n");
+         }
+         if (failed) {
+            int j;
+            printf("FAILED: ");
+            for (j=0; j < stb_arr_len(failed); ++j)
+               printf("%s ", failed[j]);
+            printf(" -- %s\n", files[i]);
+         }
       }
+      printf("Tested %d files.\n", i);
    }
    return 0;
 }

+ 3 - 2
tests/image_test.dsp

@@ -41,7 +41,7 @@ RSC=rc.exe
 # PROP Intermediate_Dir "Release"
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
-# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
 # ADD BASE RSC /l 0x409 /d "NDEBUG"
 # ADD RSC /l 0x409 /d "NDEBUG"
 BSC32=bscmake.exe
@@ -64,7 +64,8 @@ LINK32=link.exe
 # PROP Intermediate_Dir "Debug\image_test"
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
-# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c
+# SUBTRACT CPP /YX
 # ADD BASE RSC /l 0x409 /d "_DEBUG"
 # ADD RSC /l 0x409 /d "_DEBUG"
 BSC32=bscmake.exe

+ 94 - 0
tests/oversample/README.md

@@ -0,0 +1,94 @@
+# Font character oversampling for rendering from atlas textures
+
+TL,DR: Run oversample.exe on a windows machine to see the
+benefits of oversampling. It will try to use arial.ttf from the
+Windows font directory unless you type the name of a .ttf file as
+a command-line argument.
+
+## Benefits of oversampling
+
+Oversampling is a mechanism for improving subpixel rendering of characters.
+
+Improving subpixel has a few benefits:
+
+* With horizontal-oversampling, text can remain sharper while still being sub-pixel positioned for better kerning
+* Horizontally-oversampled text significantly reduces aliasing when text animates horizontally
+* Vertically-oversampled text significantly reduces aliasing when text animates vertically
+* Text oversampled in both directions significantly reduces aliasing when text rotates
+
+## What text oversampling is
+
+A common strategy for rendering text is to cache character bitmaps
+and reuse them. For hinted characters, every instance of a given
+character is always identical, so this works fine. However, stb_truetype
+doesn't do hinting.
+
+For anti-aliased characters, you can actually position the characters
+with subpixel precision, and get different bitmaps based on that positioning
+if you re-render the vector data.
+
+However, if you simply cache a single version of the bitmap and
+draw it at different subpixel positions with a GPU, you will get
+either the exact same result (if you use point-sampling on the
+texture) or linear filtering. Linear filtering will cause a sub-pixel
+positioned bitmap to blur further, causing a visible de-sharpening
+of the character. (And, since the character wasn't hinted, it was
+already blurrier than a hinted one would be, and now it gets even
+more blurry.)
+
+You can avoid this by caching multiple variants of a character which
+were rendered independently from the vector data. For example, you
+might cache 3 versions of a char, at 0, 1/3, and 2/3rds of a pixel
+horizontal offset, and always require characters to fall on integer
+positions vertically.
+
+When creating a texture atlas for use on GPUs, which support bilinear
+filtering, there is a better approach than caching several independent
+positions, which is to allow lerping between the versions to allow
+finer subpixel positioning. You can achieve these by interleaving
+each of the cached bitmaps, but this turns out to be mathematically
+equivalent to a simpler operation: oversampling and prefiltering the
+characters.
+
+So, setting oversampling of 2x2 in stb_truetype is equivalent to caching
+each character in 4 different variations, 1 for each subpixel position
+in a 2x2 set.
+
+An advantage of this formulation is that no changes are required to
+the rendering code; the exact same quad-rendering code works, it just
+uses different texture coordinates. (Note this does potentially increase
+texture bandwidth for text rendering since we end up minifying the texture
+without using mipmapping, but you probably are not going to be fill-bound
+by your text rendering.)
+
+## What about gamma?
+
+Gamma-correction for fonts just doesn't work. This doesn't seem to make
+much sense -- it's physically correct, it simulates what we'd see if you
+shrunk a font down really far, right?
+
+But you can play with it in the oversample.exe app. If you turn it on,
+white-on-black fonts become too thick (i.e. they become too bright), and
+black-on-white fonts become too thin (i.e. they are insufficiently dark). There is
+no way to adjust the font's inherent thickness (i.e. by switching to
+bold) to fix this for both; making the font thicker will make white
+text worse, and making the font thinner will make black text worse.
+Obviously you could use different fonts for light and dark cases, but
+this doesn't seem like a very good way for fonts to work.
+
+Multiple people who have experimented with this independently (me,
+Fabian Giesen,and Maxim Shemanarev of Anti-Grain Geometry) have all
+concluded that correct gamma-correction does not produce the best
+results for fonts. Font rendering just generally looks better without
+gamma correction (or possibly with some arbitrary power stuck in
+there, but it's not really correcting for gamma at that point). Maybe
+this is in part a product of how we're used to fonts being on screens
+which has changed how we expect them to look (e.g. perhaps hinting
+oversharpens them and prevents the real-world thinning you'd see in
+a black-on-white text).
+
+(AGG link on text rendering, including mention of gamma:
+  http://www.antigrain.com/research/font_rasterization/ )
+
+Nevertheless, even if you turn on gamma-correction, you will find that
+oversampling still helps in many cases for small fonts.

+ 332 - 0
tests/oversample/main.c

@@ -0,0 +1,332 @@
+#pragma warning(disable:4244; disable:4305; disable:4018)
+#include <assert.h>
+#include <ctype.h>
+
+#define STB_WINMAIN
+#include "stb_wingraph.h"
+
+#define STB_TRUETYPE_IMPLEMENTATION
+#define STB_RECT_PACK_IMPLEMENTATION
+#include "stb_rect_pack.h"
+#include "stb_truetype.h"
+
+#ifndef WINGDIAPI
+#define CALLBACK    __stdcall
+#define WINGDIAPI   __declspec(dllimport)
+#define APIENTRY    __stdcall
+#endif
+
+#include <gl/gl.h>
+#include <gl/glu.h>
+
+#define GL_FRAMEBUFFER_SRGB_EXT           0x8DB9
+
+#define SIZE_X  1024
+#define SIZE_Y  768
+
+stbtt_packedchar chardata[6][128];
+
+int sx=SIZE_X, sy=SIZE_Y;
+
+#define BITMAP_W 512
+#define BITMAP_H 512
+unsigned char temp_bitmap[BITMAP_W][BITMAP_H];
+unsigned char ttf_buffer[1 << 25];
+GLuint font_tex;
+
+float scale[2] = { 24.0f, 14.0f };
+
+int sf[6] = { 0,1,2, 0,1,2 };
+
+void load_fonts(void)
+{
+   stbtt_pack_context pc;
+   int i;
+   FILE *f;
+   char filename[256];
+   char *win = getenv("windir");
+   if (win == NULL) win = getenv("SystemRoot");
+
+   f = fopen(stb_wingraph_commandline, "rb");
+   if (!f) {
+      if (win == NULL)
+         sprintf(filename, "arial.ttf", win);
+      else
+         sprintf(filename, "%s/fonts/arial.ttf", win);
+      f = fopen(filename, "rb");
+      if (!f) exit(0);
+   }
+
+   fread(ttf_buffer, 1, 1<<25, f);
+
+   stbtt_PackBegin(&pc, temp_bitmap[0], BITMAP_W, BITMAP_H, 0, 1, NULL);
+   for (i=0; i < 2; ++i) {
+      stbtt_PackSetOversampling(&pc, 1, 1);
+      stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+0]+32);
+      stbtt_PackSetOversampling(&pc, 2, 2);
+      stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+1]+32);
+      stbtt_PackSetOversampling(&pc, 3, 1);
+      stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+2]+32);
+   }
+   stbtt_PackEnd(&pc);
+
+   glGenTextures(1, &font_tex);
+   glBindTexture(GL_TEXTURE_2D, font_tex);
+   glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, BITMAP_W, BITMAP_H, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
+   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+}
+
+int black_on_white;
+
+void draw_init(void)
+{
+   glDisable(GL_CULL_FACE);
+   glDisable(GL_TEXTURE_2D);
+   glDisable(GL_LIGHTING);
+   glDisable(GL_DEPTH_TEST);
+
+   glViewport(0,0,sx,sy);
+   if (black_on_white)
+      glClearColor(255,255,255,0);
+   else
+      glClearColor(0,0,0,0);
+   glClear(GL_COLOR_BUFFER_BIT);
+
+   glMatrixMode(GL_PROJECTION);
+   glLoadIdentity();
+   gluOrtho2D(0,sx,sy,0);
+   glMatrixMode(GL_MODELVIEW);
+   glLoadIdentity();
+}
+
+
+void drawBoxTC(float x0, float y0, float x1, float y1, float s0, float t0, float s1, float t1)
+{
+   glTexCoord2f(s0,t0); glVertex2f(x0,y0);
+   glTexCoord2f(s1,t0); glVertex2f(x1,y0);
+   glTexCoord2f(s1,t1); glVertex2f(x1,y1);
+   glTexCoord2f(s0,t1); glVertex2f(x0,y1);
+}
+
+int integer_align;
+
+void print(float x, float y, int font, char *text)
+{
+   glEnable(GL_TEXTURE_2D);
+   glBindTexture(GL_TEXTURE_2D, font_tex);
+   glBegin(GL_QUADS);
+   while (*text) {
+      stbtt_aligned_quad q;
+      stbtt_GetPackedQuad(chardata[font], BITMAP_W, BITMAP_H, *text++, &x, &y, &q, font ? 0 : integer_align);
+      drawBoxTC(q.x0,q.y0,q.x1,q.y1, q.s0,q.t0,q.s1,q.t1);
+   }
+   glEnd();
+}
+
+int font=3;
+int translating;
+int rotating=0;
+int srgb=0;
+float rotate_t, translate_t;
+int show_tex;
+
+void draw_world(void)
+{
+   int sfont = sf[font];
+   float x = 20;
+   glEnable(GL_BLEND);
+   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+   if (black_on_white)
+      glColor3f(0,0,0);
+   else
+      glColor3f(1,1,1);
+
+
+   print(80, 30, sfont, "Controls:");
+   print(100, 60, sfont, "S: toggle font size");
+   print(100, 85, sfont, "O: toggle oversampling");
+   print(100,110, sfont, "T: toggle translation");
+   print(100,135, sfont, "R: toggle rotation");
+   print(100,160, sfont, "P: toggle pixel-snap (only non-oversampled)");
+   print(100,185, sfont, "G: toggle srgb gamma-correction");
+   if (black_on_white)
+      print(100,210, sfont, "B: toggle to white-on-black");
+   else
+      print(100,210, sfont, "B: toggle to black-on-white");
+   print(100,235, sfont, "V: view font texture");
+
+   print(80, 300, sfont, "Current font:");
+
+   if (!show_tex) {
+      if (font < 3)
+         print(100, 350, sfont, "Font height: 24 pixels");
+      else
+         print(100, 350, sfont, "Font height: 14 pixels");
+   }
+
+   if (font%3==1)
+      print(100, 325, sfont, "2x2 oversampled text at 1:1");
+   else if (font%3 == 2)
+      print(100, 325, sfont, "3x1 oversampled text at 1:1");
+   else if (integer_align)
+      print(100, 325, sfont, "1:1 text, one texel = one pixel, snapped to integer coordinates");
+   else
+      print(100, 325, sfont, "1:1 text, one texel = one pixel");
+
+   if (show_tex) {
+      glBegin(GL_QUADS);
+      drawBoxTC(200,400, 200+BITMAP_W,300+BITMAP_H, 0,0,1,1);
+      glEnd();
+   } else {
+      glMatrixMode(GL_MODELVIEW);
+      glTranslatef(200,350,0);
+
+      if (translating)
+         x += fmod(translate_t*8,30);
+
+      if (rotating) {
+         glTranslatef(100,150,0);
+         glRotatef(rotate_t*2,0,0,1);
+         glTranslatef(-100,-150,0);
+      }
+      print(x,100, font, "This is a test");
+      print(x,130, font, "Now is the time for all good men to come to the aid of their country.");
+      print(x,160, font, "The quick brown fox jumps over the lazy dog.");
+      print(x,190, font, "0123456789");
+   }
+}
+
+void draw(void)
+{
+   draw_init();
+   draw_world();
+   stbwingraph_SwapBuffers(NULL);
+}
+
+static int initialized=0;
+static float last_dt;
+
+int move[4];
+int raw_mouse_x, raw_mouse_y;
+
+int loopmode(float dt, int real, int in_client)
+{
+   float actual_dt = dt;
+
+   if (!initialized) return 0;
+
+   rotate_t += dt;
+   translate_t += dt;
+
+//   music_sim();
+   if (!real)
+      return 0;
+
+   if (dt > 0.25) dt = 0.25;
+   if (dt < 0.01) dt = 0.01;
+
+   draw();
+
+   return 0;
+}
+
+int winproc(void *data, stbwingraph_event *e)
+{
+   switch (e->type) {
+      case STBWGE_create:
+         break;
+
+      case STBWGE_char:
+         switch(e->key) {
+            case 27:
+               stbwingraph_ShowCursor(NULL,1);
+               return STBWINGRAPH_winproc_exit;
+               break;
+            case 'o': case 'O':
+               font = (font+1) % 3 + (font/3)*3;
+               break;
+            case 's': case 'S':
+               font = (font+3) % 6;
+               break;
+            case 't': case 'T':
+               translating = !translating;
+               translate_t = 0;
+               break;
+            case 'r': case 'R':
+               rotating = !rotating;
+               rotate_t = 0;
+               break;
+            case 'p': case 'P':
+               integer_align = !integer_align;
+               break;
+            case 'g': case 'G':
+               srgb = !srgb;
+               if (srgb)
+                  glEnable(GL_FRAMEBUFFER_SRGB_EXT);
+               else
+                  glDisable(GL_FRAMEBUFFER_SRGB_EXT);
+               break;
+            case 'v': case 'V':
+               show_tex = !show_tex;
+               break;
+            case 'b': case 'B':
+               black_on_white = !black_on_white;
+               break;
+         }
+         break;
+
+      case STBWGE_mousemove:
+         raw_mouse_x = e->mx;
+         raw_mouse_y = e->my;
+         break;
+
+#if 0
+      case STBWGE_mousewheel:  do_mouse(e,0,0); break;
+      case STBWGE_leftdown:    do_mouse(e, 1,0); break;
+      case STBWGE_leftup:      do_mouse(e,-1,0); break;
+      case STBWGE_rightdown:   do_mouse(e,0, 1); break;
+      case STBWGE_rightup:     do_mouse(e,0,-1); break;
+#endif
+
+      case STBWGE_keydown:
+         if (e->key == VK_RIGHT) move[0] = 1;
+         if (e->key == VK_LEFT)  move[1] = 1;
+         if (e->key == VK_UP)    move[2] = 1;
+         if (e->key == VK_DOWN)  move[3] = 1;
+         break;
+      case STBWGE_keyup:
+         if (e->key == VK_RIGHT) move[0] = 0;
+         if (e->key == VK_LEFT)  move[1] = 0;
+         if (e->key == VK_UP)    move[2] = 0;
+         if (e->key == VK_DOWN)  move[3] = 0;
+         break;
+
+      case STBWGE_size:
+         sx = e->width;
+         sy = e->height;
+         loopmode(0,1,0);
+         break;
+
+      case STBWGE_draw:
+         if (initialized)
+            loopmode(0,1,0);
+         break;
+
+      default:
+         return STBWINGRAPH_unprocessed;
+   }
+   return 0;
+}
+
+void stbwingraph_main(void)
+{
+   stbwingraph_Priority(2);
+   stbwingraph_CreateWindow(1, winproc, NULL, "tt", SIZE_X,SIZE_Y, 0, 1, 0, 0);
+   stbwingraph_ShowCursor(NULL, 0);
+   load_fonts();
+   initialized = 1;
+   stbwingraph_MainLoop(loopmode, 0.016f);   // 30 fps = 0.33
+}
+

+ 97 - 0
tests/oversample/oversample.dsp

@@ -0,0 +1,97 @@
+# Microsoft Developer Studio Project File - Name="oversample" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=oversample - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "oversample.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "oversample.mak" CFG="oversample - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "oversample - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "oversample - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "oversample - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /WX /GX /O2 /I "c:\sean\prj\stb" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# SUBTRACT LINK32 /map /debug
+
+!ELSEIF  "$(CFG)" == "oversample - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /WX /Gm /GX /Zi /Od /I "c:\sean\prj\stb" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "oversample - Win32 Release"
+# Name "oversample - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\main.c
+# End Source File
+# End Target
+# End Project

+ 29 - 0
tests/oversample/oversample.dsw

@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "oversample"=.\oversample.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+

BIN
tests/oversample/oversample.exe


+ 827 - 0
tests/oversample/stb_wingraph.h

@@ -0,0 +1,827 @@
+// stb_wingraph.h  v0.01 - public domain windows graphics programming
+// wraps WinMain, ChoosePixelFormat, ChangeDisplayResolution, etc. for
+// doing OpenGL graphics
+//
+// in ONE source file, put '#define STB_DEFINE' before including this
+// OR put '#define STB_WINMAIN' to define a WinMain that calls stbwingraph_main(void)
+//
+// @TODO:
+//    2d rendering interface (that can be done easily in software)
+//    STB_WINGRAPH_SOFTWARE -- 2d software rendering only
+//    STB_WINGRAPH_OPENGL   -- OpenGL only
+
+
+#ifndef INCLUDE_STB_WINGRAPH_H
+#define INCLUDE_STB_WINGRAPH_H
+
+#ifdef STB_WINMAIN
+   #ifndef STB_DEFINE
+      #define STB_DEFINE
+      #define STB_WINGRAPH_DISABLE_DEFINE_AT_END
+   #endif
+#endif
+
+#ifdef STB_DEFINE
+   #pragma comment(lib, "opengl32.lib")
+   #pragma comment(lib, "glu32.lib")
+   #pragma comment(lib, "winmm.lib")
+#endif
+
+#ifdef __cplusplus
+#define STB_EXTERN extern "C"
+#else
+#define STB_EXTERN
+#endif
+
+#ifdef STB_DEFINE
+#ifndef _WINDOWS_
+   #ifdef APIENTRY
+   #undef APIENTRY
+   #endif
+   #ifdef WINGDIAPI
+   #undef WINGDIAPI
+   #endif
+   #define _WIN32_WINNT 0x0400  // WM_MOUSEWHEEL
+   #include <windows.h>
+#endif
+#include <stdio.h>
+#include <math.h>
+#include <time.h>
+#include <string.h>
+#include <assert.h>
+#endif
+
+typedef void * stbwingraph_hwnd;
+typedef void * stbwingraph_hinstance;
+
+enum
+{
+   STBWINGRAPH_unprocessed = -(1 << 24),
+   STBWINGRAPH_do_not_show,
+   STBWINGRAPH_winproc_exit,
+   STBWINGRAPH_winproc_update,
+   STBWINGRAPH_update_exit,
+   STBWINGRAPH_update_pause,
+};
+
+typedef enum
+{
+   STBWGE__none=0,
+
+   STBWGE_create,
+   STBWGE_create_postshow,
+   STBWGE_draw,
+   STBWGE_destroy,
+   STBWGE_char,
+   STBWGE_keydown,
+   STBWGE_syskeydown,
+   STBWGE_keyup,
+   STBWGE_syskeyup,
+   STBWGE_deactivate,
+   STBWGE_activate,
+   STBWGE_size,
+
+   STBWGE_mousemove ,
+   STBWGE_leftdown  , STBWGE_leftup  ,
+   STBWGE_middledown, STBWGE_middleup,
+   STBWGE_rightdown , STBWGE_rightup ,
+   STBWGE_mousewheel,
+} stbwingraph_event_type;
+
+typedef struct
+{
+   stbwingraph_event_type type;
+
+   // for input events (mouse, keyboard)
+   int mx,my; // mouse x & y
+   int dx,dy;
+   int shift, ctrl, alt;
+
+   // for keyboard events
+   int key;
+
+   // for STBWGE_size:
+   int width, height;
+
+   // for STBWGE_crate
+   int did_share_lists;  // if true, wglShareLists succeeded
+
+   void *handle;
+
+} stbwingraph_event;
+
+typedef int (*stbwingraph_window_proc)(void *data, stbwingraph_event *event);
+
+extern stbwingraph_hinstance   stbwingraph_app;
+extern stbwingraph_hwnd        stbwingraph_primary_window;
+extern int                     stbwingraph_request_fullscreen;
+extern int                     stbwingraph_request_windowed;
+
+STB_EXTERN void stbwingraph_ods(char *str, ...);
+STB_EXTERN int stbwingraph_MessageBox(stbwingraph_hwnd win, unsigned int type,
+                                              char *caption, char *text, ...);
+STB_EXTERN int stbwingraph_ChangeResolution(unsigned int w, unsigned int h,
+                                      unsigned int bits, int use_message_box);
+STB_EXTERN int stbwingraph_SetPixelFormat(stbwingraph_hwnd win, int color_bits,
+            int alpha_bits, int depth_bits, int stencil_bits, int accum_bits);
+STB_EXTERN int stbwingraph_DefineClass(void *hinstance, char *iconname);
+STB_EXTERN void stbwingraph_SwapBuffers(void *win);
+STB_EXTERN void stbwingraph_Priority(int n);
+
+STB_EXTERN void stbwingraph_MakeFonts(void *window, int font_base);
+STB_EXTERN void stbwingraph_ShowWindow(void *window);
+STB_EXTERN void *stbwingraph_CreateWindow(int primary, stbwingraph_window_proc func, void *data, char *text, int width, int height, int fullscreen, int resizeable, int dest_alpha, int stencil);
+STB_EXTERN void *stbwingraph_CreateWindowSimple(stbwingraph_window_proc func, int width, int height);
+STB_EXTERN void *stbwingraph_CreateWindowSimpleFull(stbwingraph_window_proc func, int fullscreen, int ww, int wh, int fw, int fh);
+STB_EXTERN void stbwingraph_DestroyWindow(void *window);
+STB_EXTERN void stbwingraph_ShowCursor(void *window, int visible);
+STB_EXTERN float stbwingraph_GetTimestep(float minimum_time);
+STB_EXTERN void stbwingraph_SetGLWindow(void *win);
+typedef int (*stbwingraph_update)(float timestep, int real, int in_client);
+STB_EXTERN int stbwingraph_MainLoop(stbwingraph_update func, float mintime);
+
+#ifdef STB_DEFINE
+stbwingraph_hinstance   stbwingraph_app;
+stbwingraph_hwnd        stbwingraph_primary_window;
+int stbwingraph_request_fullscreen;
+int stbwingraph_request_windowed;
+
+void stbwingraph_ods(char *str, ...)
+{
+   char buffer[1024];
+   va_list v;
+   va_start(v,str);
+   vsprintf(buffer, str, v);
+   va_end(v);
+   OutputDebugString(buffer);
+}
+
+int stbwingraph_MessageBox(stbwingraph_hwnd win, unsigned int type, char *caption, char *text, ...)
+{
+   va_list v;
+   char buffer[1024];
+   va_start(v, text);
+   vsprintf(buffer, text, v);
+   va_end(v);
+   return MessageBox(win, buffer, caption, type);
+}
+
+void stbwingraph_Priority(int n)
+{
+   int p;
+   switch (n) {
+      case -1: p = THREAD_PRIORITY_BELOW_NORMAL; break;
+      case 0: p = THREAD_PRIORITY_NORMAL; break;
+      case 1: p = THREAD_PRIORITY_ABOVE_NORMAL; break;
+      default:
+         if (n < 0) p = THREAD_PRIORITY_LOWEST;
+         else p = THREAD_PRIORITY_HIGHEST;
+   }
+   SetThreadPriority(GetCurrentThread(), p);
+}
+
+static void stbwingraph_ResetResolution(void)
+{
+   ChangeDisplaySettings(NULL, 0);
+}
+
+static void stbwingraph_RegisterResetResolution(void)
+{
+   static int done=0;
+   if (!done) {
+      done = 1;
+      atexit(stbwingraph_ResetResolution);
+   }
+}
+
+int stbwingraph_ChangeResolution(unsigned int w, unsigned int h, unsigned int bits, int use_message_box)
+{
+   DEVMODE mode;
+   int res;
+   
+   int i, tries=0;
+   for (i=0; ; ++i) {
+      int success = EnumDisplaySettings(NULL, i, &mode);
+      if (!success) break;
+      if (mode.dmBitsPerPel == bits && mode.dmPelsWidth == w && mode.dmPelsHeight == h) {
+         ++tries;
+         success = ChangeDisplaySettings(&mode, CDS_FULLSCREEN); 
+         if (success == DISP_CHANGE_SUCCESSFUL) {
+            stbwingraph_RegisterResetResolution();
+            return TRUE;
+         }
+         break;
+      }
+   }
+
+   if (!tries) {
+      if (use_message_box)
+         stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The resolution %d x %d x %d-bits is not supported.", w, h, bits);
+      return FALSE;
+   }
+
+   // we tried but failed, so try explicitly doing it without specifying refresh rate
+
+   // Win95 support logic
+   mode.dmBitsPerPel = bits; 
+   mode.dmPelsWidth = w; 
+   mode.dmPelsHeight = h; 
+   mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; 
+
+   res = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
+
+   switch (res) {
+      case DISP_CHANGE_SUCCESSFUL:
+         stbwingraph_RegisterResetResolution();
+         return TRUE;
+
+      case DISP_CHANGE_RESTART:
+         if (use_message_box)
+            stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "Please set your desktop to %d-bit color and then try again.");
+         return FALSE;
+
+      case DISP_CHANGE_FAILED:
+         if (use_message_box)
+            stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The hardware failed to change modes.");
+         return FALSE;
+
+      case DISP_CHANGE_BADMODE:
+         if (use_message_box)
+            stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The resolution %d x %d x %d-bits is not supported.", w, h, bits);
+         return FALSE;
+
+      default:
+         if (use_message_box)
+            stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "An unknown error prevented a change to a %d x %d x %d-bit display.", w, h, bits);
+         return FALSE;
+   }
+}
+
+int stbwingraph_SetPixelFormat(stbwingraph_hwnd win, int color_bits, int alpha_bits, int depth_bits, int stencil_bits, int accum_bits)
+{
+   HDC dc = GetDC(win);
+   PIXELFORMATDESCRIPTOR pfd = { sizeof(pfd) };
+   int                   pixel_format;
+
+   pfd.nVersion = 1;
+   pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
+   pfd.dwLayerMask = PFD_MAIN_PLANE;
+   pfd.iPixelType = PFD_TYPE_RGBA;
+   pfd.cColorBits = color_bits;
+   pfd.cAlphaBits = alpha_bits;
+   pfd.cDepthBits = depth_bits;
+   pfd.cStencilBits = stencil_bits;
+   pfd.cAccumBits = accum_bits;
+
+   pixel_format = ChoosePixelFormat(dc, &pfd);
+   if (!pixel_format) return FALSE;
+
+   if (!DescribePixelFormat(dc, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), &pfd))
+      return FALSE;
+   SetPixelFormat(dc, pixel_format, &pfd);
+
+   return TRUE;
+}
+
+typedef struct
+{
+   // app data
+   stbwingraph_window_proc func;
+   void *data;
+   // creation parameters
+   int   color, alpha, depth, stencil, accum;
+   HWND  share_window;
+   HWND  window;
+   // internal data
+   HGLRC rc;
+   HDC   dc;
+   int   hide_mouse;
+   int   in_client;
+   int   active;
+   int   did_share_lists;
+   int   mx,my; // last mouse positions
+} stbwingraph__window;
+
+static void stbwingraph__inclient(stbwingraph__window *win, int state)
+{
+   if (state != win->in_client) {
+      win->in_client = state;
+      if (win->hide_mouse)
+         ShowCursor(!state);
+   }
+}
+
+static void stbwingraph__key(stbwingraph_event *e, int type, int key, stbwingraph__window *z)
+{
+   e->type  = type;
+   e->key   = key;
+   e->shift = (GetKeyState(VK_SHIFT)   < 0);
+   e->ctrl  = (GetKeyState(VK_CONTROL) < 0);
+   e->alt   = (GetKeyState(VK_MENU)    < 0);
+   if  (z) {
+      e->mx    = z->mx;
+      e->my    = z->my;
+   } else {
+      e->mx = e->my = 0;
+   }
+   e->dx = e->dy = 0;
+}
+
+static void stbwingraph__mouse(stbwingraph_event *e, int type, WPARAM wparam, LPARAM lparam, int capture, void *wnd, stbwingraph__window *z)
+{
+   static int captured = 0;
+   e->type = type;
+   e->mx = (short) LOWORD(lparam);
+   e->my = (short) HIWORD(lparam);
+   if (!z || z->mx == -(1 << 30)) {
+      e->dx = e->dy = 0;
+   } else {
+      e->dx = e->mx - z->mx;
+      e->dy = e->my - z->my;
+   }
+   e->shift = (wparam & MK_SHIFT) != 0;
+   e->ctrl  = (wparam & MK_CONTROL) != 0;
+   e->alt   = (wparam & MK_ALT) != 0;
+   if (z) {
+      z->mx = e->mx;
+      z->my = e->my;
+   }
+   if (capture) {
+      if (!captured && capture == 1)
+         SetCapture(wnd);
+      captured += capture;
+      if (!captured && capture == -1)
+         ReleaseCapture();
+      if (captured < 0) captured = 0;
+   }
+}
+
+static void stbwingraph__mousewheel(stbwingraph_event *e, int type, WPARAM wparam, LPARAM lparam, int capture, void *wnd, stbwingraph__window *z)
+{
+   // lparam seems bogus!
+   static int captured = 0;
+   e->type = type;
+   if (z) {
+      e->mx = z->mx;
+      e->my = z->my;
+   }
+   e->dx = e->dy = 0;
+   e->shift = (wparam & MK_SHIFT) != 0;
+   e->ctrl  = (wparam & MK_CONTROL) != 0;
+   e->alt   = (GetKeyState(VK_MENU)    < 0);
+   e->key = ((int) wparam >> 16);
+}
+
+int stbwingraph_force_update;
+static int WINAPI stbwingraph_WinProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+   int allow_default = TRUE;
+   stbwingraph_event e = { STBWGE__none };
+   // the following line is wrong for 64-bit windows, but VC6 doesn't have GetWindowLongPtr
+   stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(wnd, GWL_USERDATA);
+
+   switch (msg) {
+
+      case WM_CREATE:
+      {
+         LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lparam;
+         assert(z == NULL);
+         z = (stbwingraph__window *) lpcs->lpCreateParams;
+         SetWindowLong(wnd, GWL_USERDATA, (LONG) z);
+         z->dc = GetDC(wnd);
+         if (stbwingraph_SetPixelFormat(wnd, z->color, z->alpha, z->depth, z->stencil, z->accum)) {
+            z->rc = wglCreateContext(z->dc);
+            if (z->rc) {
+               e.type = STBWGE_create;
+               z->did_share_lists = FALSE;
+               if (z->share_window) {
+                  stbwingraph__window *y = (stbwingraph__window *) GetWindowLong(z->share_window, GWL_USERDATA);
+                  if (wglShareLists(z->rc, y->rc))
+                     z->did_share_lists = TRUE;
+               }
+               wglMakeCurrent(z->dc, z->rc);
+               return 0;
+            }
+         }
+         return -1;
+      }
+
+      case WM_PAINT: {
+         PAINTSTRUCT ps;
+         HDC hdc = BeginPaint(wnd, &ps);
+         SelectObject(hdc, GetStockObject(NULL_BRUSH));
+         e.type = STBWGE_draw;
+         e.handle = wnd;
+         z->func(z->data, &e);
+         EndPaint(wnd, &ps);
+         return 0;
+      }
+
+      case WM_DESTROY:
+         e.type = STBWGE_destroy;
+         e.handle = wnd;
+         if (z && z->func)
+            z->func(z->data, &e);
+         wglMakeCurrent(NULL, NULL) ; 
+         if (z) {
+            if (z->rc) wglDeleteContext(z->rc);
+            z->dc = 0;
+            z->rc = 0;
+         }
+         if (wnd == stbwingraph_primary_window)
+            PostQuitMessage (0);
+         return 0;
+
+      case WM_CHAR:         stbwingraph__key(&e, STBWGE_char   , wparam, z); break;
+      case WM_KEYDOWN:      stbwingraph__key(&e, STBWGE_keydown, wparam, z); break;
+      case WM_KEYUP:        stbwingraph__key(&e, STBWGE_keyup  , wparam, z); break;
+
+      case WM_NCMOUSEMOVE:  stbwingraph__inclient(z,0); break;
+      case WM_MOUSEMOVE:    stbwingraph__inclient(z,1); stbwingraph__mouse(&e, STBWGE_mousemove,  wparam, lparam,0,wnd, z); break;
+      case WM_LBUTTONDOWN:  stbwingraph__mouse(&e, STBWGE_leftdown,   wparam, lparam,1,wnd, z); break;
+      case WM_MBUTTONDOWN:  stbwingraph__mouse(&e, STBWGE_middledown, wparam, lparam,1,wnd, z); break;
+      case WM_RBUTTONDOWN:  stbwingraph__mouse(&e, STBWGE_rightdown,  wparam, lparam,1,wnd, z); break;
+      case WM_LBUTTONUP:    stbwingraph__mouse(&e, STBWGE_leftup,     wparam, lparam,-1,wnd, z); break;
+      case WM_MBUTTONUP:    stbwingraph__mouse(&e, STBWGE_middleup,   wparam, lparam,-1,wnd, z); break;
+      case WM_RBUTTONUP:    stbwingraph__mouse(&e, STBWGE_rightup,    wparam, lparam,-1,wnd, z); break;
+      case WM_MOUSEWHEEL:   stbwingraph__mousewheel(&e, STBWGE_mousewheel, wparam, lparam,0,wnd, z); break;
+
+      case WM_ACTIVATE:
+         allow_default = FALSE;
+         if (LOWORD(wparam)==WA_INACTIVE ) {
+            wglMakeCurrent(z->dc, NULL);
+            e.type = STBWGE_deactivate;
+            z->active = FALSE;
+         } else {
+            wglMakeCurrent(z->dc, z->rc);
+            e.type = STBWGE_activate;
+            z->active = TRUE;
+         }
+         e.handle = wnd;
+         z->func(z->data, &e);
+         return 0;
+
+      case WM_SIZE: {
+         RECT rect;
+         allow_default = FALSE;
+         GetClientRect(wnd, &rect);
+         e.type = STBWGE_size;
+         e.width = rect.right;
+         e.height = rect.bottom;
+         e.handle = wnd;
+         z->func(z->data, &e);
+         return 0;
+      }
+
+      default:
+         return DefWindowProc (wnd, msg, wparam, lparam);
+   }
+
+   if (e.type != STBWGE__none) {
+      int n;
+      e.handle = wnd;
+      n = z->func(z->data, &e);
+      if (n == STBWINGRAPH_winproc_exit) {
+         PostQuitMessage(0);
+         n = 0;
+      }
+      if (n == STBWINGRAPH_winproc_update) {
+         stbwingraph_force_update = TRUE;
+         return 1;
+      }
+      if (n != STBWINGRAPH_unprocessed)
+         return n;
+   }
+   return DefWindowProc (wnd, msg, wparam, lparam);
+}
+
+int stbwingraph_DefineClass(HINSTANCE hInstance, char *iconname)
+{
+   WNDCLASSEX  wndclass;
+
+   stbwingraph_app = hInstance;
+
+   wndclass.cbSize        = sizeof(wndclass);
+   wndclass.style         = CS_OWNDC;
+   wndclass.lpfnWndProc   = (WNDPROC) stbwingraph_WinProc;
+   wndclass.cbClsExtra    = 0;
+   wndclass.cbWndExtra    = 0;
+   wndclass.hInstance     = hInstance;
+   wndclass.hIcon         = LoadIcon(hInstance, iconname);
+   wndclass.hCursor       = LoadCursor(NULL,IDC_ARROW);
+   wndclass.hbrBackground = GetStockObject(NULL_BRUSH);
+   wndclass.lpszMenuName  = "zwingraph";
+   wndclass.lpszClassName = "zwingraph";
+   wndclass.hIconSm       = NULL;
+
+   if (!RegisterClassEx(&wndclass))
+      return FALSE;
+   return TRUE;
+}
+
+void stbwingraph_ShowWindow(void *window)
+{
+   stbwingraph_event e = { STBWGE_create_postshow };
+   stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(window, GWL_USERDATA);
+   ShowWindow(window, SW_SHOWNORMAL);
+   InvalidateRect(window, NULL, TRUE);
+   UpdateWindow(window);
+   e.handle = window;
+   z->func(z->data, &e);
+}
+
+void *stbwingraph_CreateWindow(int primary, stbwingraph_window_proc func, void *data, char *text,
+           int width, int height, int fullscreen, int resizeable, int dest_alpha, int stencil)
+{
+   HWND win;
+   DWORD dwstyle;
+   stbwingraph__window *z = (stbwingraph__window *) malloc(sizeof(*z));
+
+   if (z == NULL) return NULL;
+   memset(z, 0, sizeof(*z));
+   z->color = 24;
+   z->depth = 24;
+   z->alpha = dest_alpha;
+   z->stencil = stencil;
+   z->func = func;
+   z->data = data;
+   z->mx = -(1 << 30);
+   z->my = 0;
+
+   if (primary) {
+      if (stbwingraph_request_windowed)
+         fullscreen = FALSE;
+      else if (stbwingraph_request_fullscreen)
+         fullscreen = TRUE;
+   }
+
+   if (fullscreen) {
+      #ifdef STB_SIMPLE
+      stbwingraph_ChangeResolution(width, height, 32, 1);
+      #else
+      if (!stbwingraph_ChangeResolution(width, height, 32, 0))
+         return NULL;
+      #endif
+      dwstyle = WS_POPUP | WS_CLIPSIBLINGS;
+   } else {
+      RECT rect;
+      dwstyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
+      if (resizeable)
+         dwstyle |= WS_SIZEBOX | WS_MAXIMIZEBOX;
+      rect.top = 0;
+      rect.left = 0;
+      rect.right = width;
+      rect.bottom = height;
+      AdjustWindowRect(&rect, dwstyle, FALSE);
+      width = rect.right - rect.left;
+      height = rect.bottom - rect.top;
+   }
+
+   win = CreateWindow("zwingraph", text ? text : "sample", dwstyle,
+                      CW_USEDEFAULT,0, width, height,
+                      NULL, NULL, stbwingraph_app, z);
+
+   if (win == NULL) return win;
+
+   if (primary) {
+      if (stbwingraph_primary_window)
+         stbwingraph_DestroyWindow(stbwingraph_primary_window);
+      stbwingraph_primary_window = win;
+   }
+
+   {
+      stbwingraph_event e = { STBWGE_create };
+      stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
+      z->window = win;
+      e.did_share_lists = z->did_share_lists;
+      e.handle = win;
+      if (z->func(z->data, &e) != STBWINGRAPH_do_not_show)
+         stbwingraph_ShowWindow(win);
+   }
+
+   return win;
+}
+
+void *stbwingraph_CreateWindowSimple(stbwingraph_window_proc func, int width, int height)
+{
+   int fullscreen = 0;
+   #ifndef _DEBUG
+   if (width ==  640 && height ==  480) fullscreen = 1;
+   if (width ==  800 && height ==  600) fullscreen = 1;
+   if (width == 1024 && height ==  768) fullscreen = 1;
+   if (width == 1280 && height == 1024) fullscreen = 1;
+   if (width == 1600 && height == 1200) fullscreen = 1;
+   //@TODO: widescreen widths
+   #endif
+   return stbwingraph_CreateWindow(1, func, NULL, NULL, width, height, fullscreen, 1, 0, 0);
+}
+
+void *stbwingraph_CreateWindowSimpleFull(stbwingraph_window_proc func, int fullscreen, int ww, int wh, int fw, int fh)
+{
+   if (fullscreen == -1) {
+   #ifdef _DEBUG
+      fullscreen = 0;
+   #else
+      fullscreen = 1;
+   #endif
+   }
+
+   if (fullscreen) {
+      if (fw) ww = fw;
+      if (fh) wh = fh;
+   }
+   return stbwingraph_CreateWindow(1, func, NULL, NULL, ww, wh, fullscreen, 1, 0, 0);
+}
+
+void stbwingraph_DestroyWindow(void *window)
+{
+   stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(window, GWL_USERDATA);
+   DestroyWindow(window);
+   free(z);
+   if (stbwingraph_primary_window == window)
+      stbwingraph_primary_window = NULL;
+}
+
+void stbwingraph_ShowCursor(void *window, int visible)
+{
+   int hide;
+   stbwingraph__window *win;
+   if (!window)
+      window = stbwingraph_primary_window;
+   win = (stbwingraph__window *) GetWindowLong((HWND) window, GWL_USERDATA);
+   hide = !visible;
+   if (hide != win->hide_mouse) {
+      win->hide_mouse = hide;
+      if (!hide)
+         ShowCursor(TRUE);
+      else if (win->in_client)
+         ShowCursor(FALSE);
+   }
+}
+
+float stbwingraph_GetTimestep(float minimum_time)
+{
+   float elapsedTime;
+   double thisTime;
+   static double lastTime = -1;
+   
+   if (lastTime == -1)
+      lastTime = timeGetTime() / 1000.0 - minimum_time;
+
+   for(;;) {
+      thisTime = timeGetTime() / 1000.0;
+      elapsedTime = (float) (thisTime - lastTime);
+      if (elapsedTime >= minimum_time) {
+         lastTime = thisTime;         
+         return elapsedTime;
+      }
+      #if 1
+      Sleep(2);
+      #endif
+   }
+}
+
+void stbwingraph_SetGLWindow(void *win)
+{
+   stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
+   if (z)
+      wglMakeCurrent(z->dc, z->rc);
+}
+
+void stbwingraph_MakeFonts(void *window, int font_base)
+{
+   wglUseFontBitmaps(GetDC(window ? window : stbwingraph_primary_window), 0, 256, font_base);
+}
+
+// returns 1 if WM_QUIT, 0 if 'func' returned 0
+int stbwingraph_MainLoop(stbwingraph_update func, float mintime)
+{
+   int needs_drawing = FALSE;
+   MSG msg;
+
+   int is_animating = TRUE;
+   if (mintime <= 0) mintime = 0.01f;
+
+   for(;;) {
+      int n;
+
+      is_animating = TRUE;
+      // wait for a message if: (a) we're animating and there's already a message
+      // or (b) we're not animating
+      if (!is_animating || PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
+         stbwingraph_force_update = FALSE;
+         if (GetMessage(&msg, NULL, 0, 0)) {
+            TranslateMessage(&msg);
+            DispatchMessage(&msg);
+         } else {
+            return 1;   // WM_QUIT
+         }
+
+         // only force a draw for certain messages...
+         // if I don't do this, we peg at 50% for some reason... must
+         // be a bug somewhere, because we peg at 100% when rendering...
+         // very weird... looks like NVIDIA is pumping some messages
+         // through our pipeline? well, ok, I guess if we can get
+         // non-user-generated messages we have to do this
+         if (!stbwingraph_force_update) {
+            switch (msg.message) {
+               case WM_MOUSEMOVE:
+               case WM_NCMOUSEMOVE:
+                  break;
+               case WM_CHAR:         
+               case WM_KEYDOWN:      
+               case WM_KEYUP:        
+               case WM_LBUTTONDOWN:  
+               case WM_MBUTTONDOWN:  
+               case WM_RBUTTONDOWN:  
+               case WM_LBUTTONUP:    
+               case WM_MBUTTONUP:    
+               case WM_RBUTTONUP:    
+               case WM_TIMER:
+               case WM_SIZE:
+               case WM_ACTIVATE:
+                  needs_drawing = TRUE;
+                  break;
+            }
+         } else
+            needs_drawing = TRUE;
+      }
+
+      // if another message, process that first
+      // @TODO: i don't think this is working, because I can't key ahead
+      // in the SVT demo app
+      if (PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE))
+         continue;
+
+      // and now call update
+      if (needs_drawing || is_animating) {
+         int real=1, in_client=1;
+         if (stbwingraph_primary_window) {
+            stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(stbwingraph_primary_window, GWL_USERDATA);
+            if (z && !z->active) {
+               real = 0;
+            }
+            if (z)
+               in_client = z->in_client;
+         }
+
+         if (stbwingraph_primary_window)
+            stbwingraph_SetGLWindow(stbwingraph_primary_window);
+         n = func(stbwingraph_GetTimestep(mintime), real, in_client);
+         if (n == STBWINGRAPH_update_exit)
+            return 0; // update_quit
+
+         is_animating = (n != STBWINGRAPH_update_pause);
+
+         needs_drawing = FALSE;
+      }
+   }
+}
+
+void stbwingraph_SwapBuffers(void *win)
+{
+   stbwingraph__window *z;
+   if (win == NULL) win = stbwingraph_primary_window;
+   z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
+   if (z && z->dc)
+      SwapBuffers(z->dc);
+}
+#endif
+
+#ifdef STB_WINMAIN    
+void stbwingraph_main(void);
+
+char *stb_wingraph_commandline;
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
+{
+   {
+      char buffer[1024];
+      // add spaces to either side of the string
+      buffer[0] = ' ';
+      strcpy(buffer+1, lpCmdLine);
+      strcat(buffer, " ");
+      if (strstr(buffer, " -reset ")) {
+         ChangeDisplaySettings(NULL, 0); 
+         exit(0);
+      }
+      if (strstr(buffer, " -window ") || strstr(buffer, " -windowed "))
+         stbwingraph_request_windowed = TRUE;
+      else if (strstr(buffer, " -full ") || strstr(buffer, " -fullscreen "))
+         stbwingraph_request_fullscreen = TRUE;
+   }
+   stb_wingraph_commandline = lpCmdLine;
+
+   stbwingraph_DefineClass(hInstance, "appicon");
+   stbwingraph_main();
+
+   return 0;
+}
+#endif
+
+#undef STB_EXTERN
+#ifdef STB_WINGRAPH_DISABLE_DEFINE_AT_END
+#undef STB_DEFINE
+#endif
+
+#endif // INCLUDE_STB_WINGRAPH_H

BIN
tests/pngsuite/16bit/basi0g16.png


BIN
tests/pngsuite/16bit/basi2c16.png


BIN
tests/pngsuite/16bit/basi4a16.png


BIN
tests/pngsuite/16bit/basi6a16.png


BIN
tests/pngsuite/16bit/basn0g16.png


BIN
tests/pngsuite/16bit/basn2c16.png


BIN
tests/pngsuite/16bit/basn4a16.png


BIN
tests/pngsuite/16bit/basn6a16.png


BIN
tests/pngsuite/16bit/bgai4a16.png


BIN
tests/pngsuite/16bit/bgan6a16.png


BIN
tests/pngsuite/16bit/bggn4a16.png


BIN
tests/pngsuite/16bit/bgyn6a16.png


BIN
tests/pngsuite/16bit/oi1n0g16.png


BIN
tests/pngsuite/16bit/oi1n2c16.png


BIN
tests/pngsuite/16bit/oi2n0g16.png


BIN
tests/pngsuite/16bit/oi2n2c16.png


BIN
tests/pngsuite/16bit/oi4n0g16.png


BIN
tests/pngsuite/16bit/oi4n2c16.png


BIN
tests/pngsuite/16bit/oi9n0g16.png


BIN
tests/pngsuite/16bit/oi9n2c16.png


BIN
tests/pngsuite/16bit/tbbn2c16.png


BIN
tests/pngsuite/16bit/tbgn2c16.png


BIN
tests/pngsuite/16bit/tbwn0g16.png


+ 9 - 0
tests/pngsuite/PngSuite.LICENSE

@@ -0,0 +1,9 @@
+PngSuite
+--------
+
+Permission to use, copy, modify and distribute these images for any
+purpose and without fee is hereby granted.
+
+
+(c) Willem van Schaik, 1996, 2011
+

BIN
tests/pngsuite/corrupt/xc1n0g08.png


BIN
tests/pngsuite/corrupt/xc9n2c08.png


BIN
tests/pngsuite/corrupt/xcrn0g04.png


BIN
tests/pngsuite/corrupt/xcsn0g01.png


BIN
tests/pngsuite/corrupt/xd0n2c08.png


BIN
tests/pngsuite/corrupt/xd3n2c08.png


BIN
tests/pngsuite/corrupt/xd9n2c08.png


BIN
tests/pngsuite/corrupt/xdtn0g01.png


BIN
tests/pngsuite/corrupt/xhdn0g08.png


BIN
tests/pngsuite/corrupt/xlfn0g04.png


BIN
tests/pngsuite/corrupt/xs1n0g01.png


BIN
tests/pngsuite/corrupt/xs2n0g01.png


BIN
tests/pngsuite/corrupt/xs4n0g01.png


BIN
tests/pngsuite/corrupt/xs7n0g01.png


BIN
tests/pngsuite/primary/basi0g01.png


BIN
tests/pngsuite/primary/basi0g02.png


BIN
tests/pngsuite/primary/basi0g04.png


BIN
tests/pngsuite/primary/basi0g08.png


BIN
tests/pngsuite/primary/basi2c08.png


BIN
tests/pngsuite/primary/basi3p01.png


BIN
tests/pngsuite/primary/basi3p02.png


BIN
tests/pngsuite/primary/basi3p04.png


BIN
tests/pngsuite/primary/basi3p08.png


BIN
tests/pngsuite/primary/basi4a08.png


BIN
tests/pngsuite/primary/basi6a08.png


BIN
tests/pngsuite/primary/basn0g01.png


BIN
tests/pngsuite/primary/basn0g02.png


BIN
tests/pngsuite/primary/basn0g04.png


BIN
tests/pngsuite/primary/basn0g08.png


BIN
tests/pngsuite/primary/basn2c08.png


BIN
tests/pngsuite/primary/basn3p01.png


BIN
tests/pngsuite/primary/basn3p02.png


BIN
tests/pngsuite/primary/basn3p04.png


BIN
tests/pngsuite/primary/basn3p08.png


BIN
tests/pngsuite/primary/basn4a08.png


BIN
tests/pngsuite/primary/basn6a08.png


BIN
tests/pngsuite/primary/bgai4a08.png


BIN
tests/pngsuite/primary/bgan6a08.png


BIN
tests/pngsuite/primary/bgbn4a08.png


BIN
tests/pngsuite/primary/bgwn6a08.png


BIN
tests/pngsuite/primary/s01i3p01.png


BIN
tests/pngsuite/primary/s01n3p01.png


BIN
tests/pngsuite/primary/s02i3p01.png


BIN
tests/pngsuite/primary/s02n3p01.png


BIN
tests/pngsuite/primary/s03i3p01.png


BIN
tests/pngsuite/primary/s03n3p01.png


BIN
tests/pngsuite/primary/s04i3p01.png


BIN
tests/pngsuite/primary/s04n3p01.png


BIN
tests/pngsuite/primary/s05i3p02.png


BIN
tests/pngsuite/primary/s05n3p02.png


BIN
tests/pngsuite/primary/s06i3p02.png


BIN
tests/pngsuite/primary/s06n3p02.png


BIN
tests/pngsuite/primary/s07i3p02.png


BIN
tests/pngsuite/primary/s07n3p02.png


BIN
tests/pngsuite/primary/s08i3p02.png


BIN
tests/pngsuite/primary/s08n3p02.png


BIN
tests/pngsuite/primary/s09i3p02.png


BIN
tests/pngsuite/primary/s09n3p02.png


BIN
tests/pngsuite/primary/s32i3p04.png


BIN
tests/pngsuite/primary/s32n3p04.png


BIN
tests/pngsuite/primary/s33i3p04.png


BIN
tests/pngsuite/primary/s33n3p04.png


Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно