fix 1/2/4-bit png to filter bytes before decoding to pixels;

rename pngsuite/part1 to pngsuite/primary;
check in pngsuite
pull/60/head
Sean Barrett 2014-12-14 01:43:23 -08:00
parent 01d2c9d957
commit 8ac015c03f
254 changed files with 154 additions and 88 deletions

View File

@ -2487,133 +2487,184 @@ 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, int depth, int color)
{
stbi__context *s = a->s;
stbi__uint32 i,j,stride = x*out_n;
stbi__uint32 img_len;
stbi__uint32 img_len, img_width_bytes;
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);
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_len = ((((img_n * x * depth) + 7) >> 3) + 1) * y;
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_len) return stbi__err("not enough pixels","Corrupt PNG");
} else { // interlaced:
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) {
if (depth != 8) free(line8);
int filter_bytes = img_n;
int width = x;
if (filter > 4)
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
stbi_uc * decode_out = line8;
stbi_uc scale = (color == 0) ? 0xFF/((1<<depth)-1) : 1; // scale grayscale values to 0..255 range
in = line8;
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 (depth < 8) {
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] = 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;
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;
}
}
if (img_n != out_n) cur[img_n] = 255;
in += 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;
}
// @TODO: special case filter_bytes = 1, or just rewrite whole thing to not use a nested loop
// 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) {
#define CASE(f) \
case f: \
for (i=x-1; i >= 1; --i, in+=img_n,cur+=img_n,prior+=img_n) \
for (k=0; k < img_n; ++k)
for (i=width-1; i >= 1; --i, raw+=filter_bytes,cur+=filter_bytes,prior+=filter_bytes) \
for (k=0; k < filter_bytes; ++k)
switch (filter) {
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;
CASE(STBI__F_none) cur[k] = raw[k]; 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-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
} else {
STBI_ASSERT(img_n+1 == out_n);
#define CASE(f) \
case f: \
for (i=x-1; i >= 1; --i, cur[img_n]=255,in+=img_n,cur+=out_n,prior+=out_n) \
for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \
for (k=0; k < img_n; ++k)
switch (filter) {
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;
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;
}
#undef CASE
}
}
if (depth != 8) free(line8);
// 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;
}
@ -2856,7 +2907,7 @@ 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;
}

View File

@ -7,7 +7,7 @@
#define STB_DEFINE
#include "stb.h"
#define PART1
#define PNGSUITE_PRIMARY
int main(int argc, char **argv)
{
@ -34,8 +34,8 @@ int main(int argc, char **argv)
}
} else {
int i, nope=0;
#ifdef PART1
char **files = stb_readdir_files("pngsuite/part1");
#ifdef PNGSUITE_PRIMARY
char **files = stb_readdir_files("pngsuite/primary");
#else
char **files = stb_readdir_files("images");
#endif
@ -43,7 +43,7 @@ int main(int argc, char **argv)
int n;
char **failed = NULL;
unsigned char *data;
//printf("%s\n", files[i]);
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");
@ -52,16 +52,22 @@ int main(int argc, char **argv)
if (data) {
char fname[512];
#ifdef PART1
#ifdef PNGSUITE_PRIMARY
int w2,h2;
unsigned char *data2;
stb_splitpath(fname, files[i], STB_FILE_EXT);
data2 = stbi_load(stb_sprintf("pngsuite/part1_check/%s", fname), &w2, &h2, 0, 4);
data2 = stbi_load(stb_sprintf("pngsuite/primary_check/%s", fname), &w2, &h2, 0, 4);
if (!data2)
printf("FAILED: couldn't load 'pngsuite/part1_check/%s\n", fname);
printf("FAILED: couldn't load 'pngsuite/primary_check/%s\n", fname);
else {
if (w != w2 || h != w2 || 0 != memcmp(data, data2, w*h*4)) {
printf("FAILED: %s loaded but didn't match part1_check 32-bit version\n", files[i]);
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);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

Some files were not shown because too many files have changed in this diff Show More