mirror of
https://github.com/nothings/stb
synced 2024-12-15 04:22:35 +03:00
Support for 1/2/4-bit palettized PNG
This commit is contained in:
parent
655d24552a
commit
f2b3ebd470
110
stb_image.h
110
stb_image.h
@ -63,7 +63,7 @@
|
|||||||
James "moose2000" Brown (iPhone PNG) David Woo
|
James "moose2000" Brown (iPhone PNG) David Woo
|
||||||
Ben "Disch" Wenger (io callbacks) Roy Eltham
|
Ben "Disch" Wenger (io callbacks) Roy Eltham
|
||||||
Martin "SpartanJ" Golini Luke Graham
|
Martin "SpartanJ" Golini Luke Graham
|
||||||
Thomas Ruf
|
Omar Cornut (1/2/4-bit palettized PNG) Thomas Ruf
|
||||||
John Bartholomew
|
John Bartholomew
|
||||||
Optimizations & bugfixes Ken Hamada
|
Optimizations & bugfixes Ken Hamada
|
||||||
Fabian "ryg" Giesen Cort Stratton
|
Fabian "ryg" Giesen Cort Stratton
|
||||||
@ -2487,20 +2487,40 @@ static int stbi__paeth(int a, int b, int c)
|
|||||||
#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings
|
#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings
|
||||||
|
|
||||||
// create the png data from post-deflated data
|
// 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)
|
||||||
{
|
{
|
||||||
stbi__context *s = a->s;
|
stbi__context *s = a->s;
|
||||||
stbi__uint32 i,j,stride = x*out_n;
|
stbi__uint32 i,j,stride = x*out_n;
|
||||||
|
stbi__uint32 img_len;
|
||||||
int k;
|
int k;
|
||||||
int img_n = s->img_n; // copy it into a local for later
|
int img_n = s->img_n; // copy it into a local for later
|
||||||
|
int addr_shift;
|
||||||
|
unsigned int pixel_data_shift_addr_mask;
|
||||||
|
unsigned int pixel_data_shift_addr_lshift;
|
||||||
|
stbi_uc pixel_data_mask;
|
||||||
|
|
||||||
STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1);
|
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);
|
||||||
if (!a->out) return stbi__err("outofmem", "Out of memory");
|
if (!a->out) return stbi__err("outofmem", "Out of memory");
|
||||||
|
|
||||||
|
img_len = (img_n * x) * y;
|
||||||
|
if (depth<8) img_len /= (8/depth);
|
||||||
|
else if (depth>8) img_len *= depth>>3;
|
||||||
|
img_len += y;
|
||||||
if (s->img_x == x && s->img_y == 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:
|
} 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (depth)
|
||||||
|
{
|
||||||
|
case 8: addr_shift = 0; pixel_data_shift_addr_mask = 0x00; pixel_data_shift_addr_lshift = 0; pixel_data_mask = 0xFF; break;
|
||||||
|
case 4: addr_shift = 1; pixel_data_shift_addr_mask = 0x01; pixel_data_shift_addr_lshift = 2; pixel_data_mask = 0x0F; break;
|
||||||
|
case 2: addr_shift = 2; pixel_data_shift_addr_mask = 0x03; pixel_data_shift_addr_lshift = 1; pixel_data_mask = 0x03; break;
|
||||||
|
case 1: addr_shift = 3; pixel_data_shift_addr_mask = 0x07; pixel_data_shift_addr_lshift = 0; pixel_data_mask = 0x01; break;
|
||||||
|
}
|
||||||
|
|
||||||
for (j=0; j < y; ++j) {
|
for (j=0; j < y; ++j) {
|
||||||
stbi_uc *cur = a->out + stride*j;
|
stbi_uc *cur = a->out + stride*j;
|
||||||
stbi_uc *prior = cur - stride;
|
stbi_uc *prior = cur - stride;
|
||||||
@ -2508,65 +2528,74 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
|
|||||||
if (filter > 4) return stbi__err("invalid filter","Corrupt PNG");
|
if (filter > 4) return stbi__err("invalid filter","Corrupt PNG");
|
||||||
// if first row, use special filter that doesn't sample previous row
|
// if first row, use special filter that doesn't sample previous row
|
||||||
if (j == 0) filter = first_row_filter[filter];
|
if (j == 0) filter = first_row_filter[filter];
|
||||||
|
|
||||||
|
// Expanding the macro for reference (probably worth inlining the whole loop or at least splitting 8 vs 1/2/4)
|
||||||
|
// - Depth 8 (((ARR[K ])
|
||||||
|
// - Depth 4 (((ARR[K >> 1]) >> ((k & 0x01) << 2)) & 0x0F)
|
||||||
|
// - Depth 2 (((ARR[K >> 2]) >> ((k & 0x03) << 1)) & 0x03)
|
||||||
|
// - Depth 1 (((ARR[K >> 3]) >> ((k & 0x07) ) & 0x01)
|
||||||
|
#define PIXEL(ARR,K) (((ARR[(K) >> addr_shift]) >> (((7-K) & pixel_data_shift_addr_mask) << pixel_data_shift_addr_lshift)) & pixel_data_mask)
|
||||||
|
|
||||||
// handle first pixel explicitly
|
// handle first pixel explicitly
|
||||||
for (k=0; k < img_n; ++k) {
|
int rawk=0;
|
||||||
|
for (k=0; k < img_n; ++k, ++rawk) {
|
||||||
switch (filter) {
|
switch (filter) {
|
||||||
case STBI__F_none : cur[k] = raw[k]; break;
|
case STBI__F_none : cur[k] = PIXEL(raw,rawk); break;
|
||||||
case STBI__F_sub : cur[k] = raw[k]; break;
|
case STBI__F_sub : cur[k] = PIXEL(raw,rawk); break;
|
||||||
case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
|
case STBI__F_up : cur[k] = STBI__BYTECAST(PIXEL(raw,rawk) + prior[k]); break;
|
||||||
case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break;
|
case STBI__F_avg : cur[k] = STBI__BYTECAST(PIXEL(raw,rawk) + (prior[k]>>1)); break;
|
||||||
case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break;
|
case STBI__F_paeth : cur[k] = STBI__BYTECAST(PIXEL(raw,rawk) + stbi__paeth(0,prior[k],0)); break;
|
||||||
case STBI__F_avg_first : cur[k] = raw[k]; break;
|
case STBI__F_avg_first : cur[k] = PIXEL(raw,rawk); break;
|
||||||
case STBI__F_paeth_first: cur[k] = raw[k]; break;
|
case STBI__F_paeth_first: cur[k] = PIXEL(raw,rawk); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (img_n != out_n) cur[img_n] = 255;
|
if (img_n != out_n) cur[img_n] = 255;
|
||||||
raw += img_n;
|
|
||||||
cur += out_n;
|
cur += out_n;
|
||||||
prior += out_n;
|
prior += out_n;
|
||||||
// this is a little gross, so that we don't switch per-pixel or per-component
|
// this is a little gross, so that we don't switch per-pixel or per-component
|
||||||
if (img_n == out_n) {
|
if (img_n == out_n) {
|
||||||
#define CASE(f) \
|
#define CASE(f) \
|
||||||
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, cur+=img_n,prior+=img_n) \
|
||||||
for (k=0; k < img_n; ++k)
|
for (k=0; k < img_n; ++k, ++rawk)
|
||||||
switch (filter) {
|
switch (filter) {
|
||||||
CASE(STBI__F_none) cur[k] = raw[k]; break;
|
CASE(STBI__F_none) cur[k] = PIXEL(raw,rawk); break;
|
||||||
CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-img_n]); break;
|
CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(PIXEL(raw,rawk) + cur[k-img_n]); break;
|
||||||
CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
|
CASE(STBI__F_up) cur[k] = STBI__BYTECAST(PIXEL(raw,rawk) + prior[k]); break;
|
||||||
CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-img_n])>>1)); break;
|
CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(PIXEL(raw,rawk) + ((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_paeth) cur[k] = STBI__BYTECAST(PIXEL(raw,rawk) + 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_avg_first) cur[k] = STBI__BYTECAST(PIXEL(raw,rawk) + (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_paeth_first) cur[k] = STBI__BYTECAST(PIXEL(raw,rawk) + stbi__paeth(cur[k-img_n],0,0)); break;
|
||||||
}
|
}
|
||||||
#undef CASE
|
#undef CASE
|
||||||
} else {
|
} else {
|
||||||
STBI_ASSERT(img_n+1 == out_n);
|
STBI_ASSERT(img_n+1 == out_n);
|
||||||
#define CASE(f) \
|
#define CASE(f) \
|
||||||
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,cur+=out_n,prior+=out_n) \
|
||||||
for (k=0; k < img_n; ++k)
|
for (k=0; k < img_n; ++k, ++rawk)
|
||||||
switch (filter) {
|
switch (filter) {
|
||||||
CASE(STBI__F_none) cur[k] = raw[k]; break;
|
CASE(STBI__F_none) cur[k] = PIXEL(raw,k); break;
|
||||||
CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-out_n]); break;
|
CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(PIXEL(raw,rawk) + cur[k-out_n]); break;
|
||||||
CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
|
CASE(STBI__F_up) cur[k] = STBI__BYTECAST(PIXEL(raw,rawk) + prior[k]); break;
|
||||||
CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-out_n])>>1)); break;
|
CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(PIXEL(raw,rawk) + ((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_paeth) cur[k] = STBI__BYTECAST(PIXEL(raw,rawk) + 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_avg_first) cur[k] = STBI__BYTECAST(PIXEL(raw,rawk) + (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_paeth_first) cur[k] = STBI__BYTECAST(PIXEL(raw,rawk) + stbi__paeth(cur[k-out_n],0,0)); break;
|
||||||
}
|
}
|
||||||
#undef CASE
|
#undef CASE
|
||||||
}
|
}
|
||||||
|
raw+=(rawk+pixel_data_shift_addr_mask)>>addr_shift; // scanlines are aligned on byte boundaries
|
||||||
}
|
}
|
||||||
return 1;
|
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 interlaced)
|
||||||
{
|
{
|
||||||
stbi_uc *final;
|
stbi_uc *final;
|
||||||
int p;
|
int p;
|
||||||
if (!interlaced)
|
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);
|
||||||
|
|
||||||
// de-interlacing
|
// de-interlacing
|
||||||
final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n);
|
final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n);
|
||||||
@ -2580,7 +2609,7 @@ 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];
|
x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p];
|
||||||
y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p];
|
y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p];
|
||||||
if (x && y) {
|
if (x && y) {
|
||||||
if (!stbi__create_png_image_raw(a, raw, raw_len, out_n, x, y)) {
|
if (!stbi__create_png_image_raw(a, raw, raw_len, out_n, x, y, depth)) {
|
||||||
free(final);
|
free(final);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2720,7 +2749,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
|
|||||||
stbi_uc palette[1024], pal_img_n=0;
|
stbi_uc palette[1024], pal_img_n=0;
|
||||||
stbi_uc has_trans=0, tc[3];
|
stbi_uc has_trans=0, tc[3];
|
||||||
stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0;
|
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, depth=0, is_iphone=0;
|
||||||
stbi__context *s = z->s;
|
stbi__context *s = z->s;
|
||||||
|
|
||||||
z->expanded = NULL;
|
z->expanded = NULL;
|
||||||
@ -2739,14 +2768,19 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
|
|||||||
stbi__skip(s, c.length);
|
stbi__skip(s, c.length);
|
||||||
break;
|
break;
|
||||||
case PNG_TYPE('I','H','D','R'): {
|
case PNG_TYPE('I','H','D','R'): {
|
||||||
int depth,color,comp,filter;
|
int color,comp,filter;
|
||||||
if (!first) return stbi__err("multiple IHDR","Corrupt PNG");
|
if (!first) return stbi__err("multiple IHDR","Corrupt PNG");
|
||||||
first = 0;
|
first = 0;
|
||||||
if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG");
|
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_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?)");
|
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);
|
||||||
color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG");
|
color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG");
|
||||||
|
if (color == 3) {
|
||||||
|
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 for palettized images"); // support 1/2/4 bpp for palettized.
|
||||||
|
} else {
|
||||||
|
if (depth != 8) return stbi__err("8-bit only","PNG not supported: 8-bit only"); // greyscale images (color==0) would need the pixel data to be scaled (see PIXEL macro)
|
||||||
|
}
|
||||||
if (color == 3) pal_img_n = 3; else if (color & 1) 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");
|
comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG");
|
||||||
filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG");
|
filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG");
|
||||||
@ -2829,7 +2863,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
|
|||||||
s->img_out_n = s->img_n+1;
|
s->img_out_n = s->img_n+1;
|
||||||
else
|
else
|
||||||
s->img_out_n = s->img_n;
|
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, interlace)) return 0;
|
||||||
if (has_trans)
|
if (has_trans)
|
||||||
if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0;
|
if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0;
|
||||||
if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2)
|
if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2)
|
||||||
|
Loading…
Reference in New Issue
Block a user