Fix stb_image to load 1/2/4 bpp PNG (unofficial fix, submitted as PR)
diff --git a/examples/opengl_example/stb_image.h b/examples/opengl_example/stb_image.h
index 90d0d15..5ab9c58 100644
--- a/examples/opengl_example/stb_image.h
+++ b/examples/opengl_example/stb_image.h
@@ -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,6 +28,7 @@
- overridable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD)
Latest revisions:
+ 1.xx (2014-09-26) 1/2/4-bit PNG support (both grayscale and paletted)
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
@@ -63,7 +64,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
@@ -2487,86 +2488,141 @@
#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings
// 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;
int k;
int img_n = s->img_n; // copy it into a local for later
+ stbi_uc* line8 = NULL; // point into raw when depth==8 else temporary local buffer
+
STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1);
a->out = (stbi_uc *) stbi__malloc(x * y * out_n);
if (!a->out) return stbi__err("outofmem", "Out of memory");
+
+ img_len = ((((img_n * x * depth) + 7) >> 3) + 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");
}
+
+ if (depth != 8) {
+ line8 = (stbi_uc *) stbi__malloc((x+7) * out_n); // allocate buffer for one scanline
+ if (!line8) return stbi__err("outofmem", "Out of memory");
+ }
+
for (j=0; j < y; ++j) {
+ stbi_uc *in;
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");
+ if (filter > 4) {
+ if (depth != 8) free(line8);
+ return stbi__err("invalid filter","Corrupt PNG");
+ }
+
+ if (depth == 8) {
+ in = raw;
+ raw += x*img_n;
+ }
+ else {
+ // 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
+ in = line8;
+ stbi_uc* decode_out = line8;
+ stbi_uc scale = (color == 0) ? 0xFF/((1<<depth)-1) : 1; // scale grayscale values to 0..255 range
+ if (depth == 4) {
+ for (k=x*img_n; k >= 1; k-=2, raw++) {
+ *decode_out++ = scale * ((*raw >> 4) );
+ *decode_out++ = scale * ((*raw ) & 0x0f);
+ }
+ } else if (depth == 2) {
+ for (k=x*img_n; k >= 1; k-=4, raw++) {
+ *decode_out++ = scale * ((*raw >> 6) );
+ *decode_out++ = scale * ((*raw >> 4) & 0x03);
+ *decode_out++ = scale * ((*raw >> 2) & 0x03);
+ *decode_out++ = scale * ((*raw ) & 0x03);
+ }
+ } else if (depth == 1) {
+ for (k=x*img_n; k >= 1; k-=8, raw++) {
+ *decode_out++ = scale * ((*raw >> 7) );
+ *decode_out++ = scale * ((*raw >> 6) & 0x01);
+ *decode_out++ = scale * ((*raw >> 5) & 0x01);
+ *decode_out++ = scale * ((*raw >> 4) & 0x01);
+ *decode_out++ = scale * ((*raw >> 3) & 0x01);
+ *decode_out++ = scale * ((*raw >> 2) & 0x01);
+ *decode_out++ = scale * ((*raw >> 1) & 0x01);
+ *decode_out++ = scale * ((*raw ) & 0x01);
+ }
+ }
+ }
+
// 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) {
switch (filter) {
- case STBI__F_none : cur[k] = raw[k]; break;
- case STBI__F_sub : cur[k] = raw[k]; 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]>>1)); break;
- case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break;
- case STBI__F_avg_first : cur[k] = raw[k]; break;
- case STBI__F_paeth_first: cur[k] = raw[k]; break;
+ case STBI__F_none : cur[k] = in[k]; break;
+ case STBI__F_sub : cur[k] = in[k]; break;
+ case STBI__F_up : cur[k] = STBI__BYTECAST(in[k] + prior[k]); break;
+ case STBI__F_avg : cur[k] = STBI__BYTECAST(in[k] + (prior[k]>>1)); break;
+ case STBI__F_paeth : cur[k] = STBI__BYTECAST(in[k] + stbi__paeth(0,prior[k],0)); break;
+ case STBI__F_avg_first : cur[k] = in[k]; break;
+ case STBI__F_paeth_first: cur[k] = in[k]; break;
}
}
if (img_n != out_n) cur[img_n] = 255;
- raw += img_n;
+ in += img_n;
cur += out_n;
prior += out_n;
// this is a little gross, so that we don't switch per-pixel or per-component
if (img_n == out_n) {
#define CASE(f) \
case f: \
- for (i=x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \
+ for (i=x-1; i >= 1; --i, in+=img_n,cur+=img_n,prior+=img_n) \
for (k=0; k < img_n; ++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;
- 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_none) cur[k] = in[k]; break;
+ CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(in[k] + cur[k-img_n]); break;
+ CASE(STBI__F_up) cur[k] = STBI__BYTECAST(in[k] + prior[k]); break;
+ CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(in[k] + ((prior[k] + cur[k-img_n])>>1)); break;
+ CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(in[k] + stbi__paeth(cur[k-img_n],prior[k],prior[k-img_n])); break;
+ CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(in[k] + (cur[k-img_n] >> 1)); break;
+ CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(in[k] + stbi__paeth(cur[k-img_n],0,0)); break;
}
#undef CASE
} else {
STBI_ASSERT(img_n+1 == out_n);
#define CASE(f) \
case f: \
- for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \
+ for (i=x-1; i >= 1; --i, cur[img_n]=255,in+=img_n,cur+=out_n,prior+=out_n) \
for (k=0; k < img_n; ++k)
switch (filter) {
- CASE(STBI__F_none) cur[k] = raw[k]; break;
- CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-out_n]); 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-out_n])>>1)); break;
- CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); break;
- CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-out_n] >> 1)); break;
- CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],0,0)); break;
+ CASE(STBI__F_none) cur[k] = in[k]; break;
+ CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(in[k] + cur[k-out_n]); break;
+ CASE(STBI__F_up) cur[k] = STBI__BYTECAST(in[k] + prior[k]); break;
+ CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(in[k] + ((prior[k] + cur[k-out_n])>>1)); break;
+ CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(in[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); break;
+ CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(in[k] + (cur[k-out_n] >> 1)); break;
+ CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(in[k] + stbi__paeth(cur[k-out_n],0,0)); break;
}
#undef CASE
}
}
+
+ if (depth != 8) free(line8);
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 *raw, stbi__uint32 raw_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, raw, raw_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);
@@ -2580,7 +2636,8 @@
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 = ((((out_n * x * depth) + 7) >> 3) + 1) * y;
+ if (!stbi__create_png_image_raw(a, raw, raw_len, out_n, x, y, depth, color)) {
free(final);
return 0;
}
@@ -2589,8 +2646,8 @@
memcpy(final + (j*yspc[p]+yorig[p])*a->s->img_x*out_n + (i*xspc[p]+xorig[p])*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;
+ raw += img_len;
+ raw_len -= img_len;
}
}
a->out = final;
@@ -2720,7 +2777,7 @@
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;
@@ -2739,13 +2796,13 @@
stbi__skip(s, c.length);
break;
case PNG_TYPE('I','H','D','R'): {
- int depth,color,comp,filter;
+ 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");
@@ -2829,7 +2886,7 @@
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)
@@ -4570,7 +4627,7 @@
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)