Last active
February 25, 2020 00:35
-
-
Save superllama/8b2c16dda9d807c57940026a9c061975 to your computer and use it in GitHub Desktop.
A PNG Decoder I wrote years ago that has an unfindable bug with some images and also relies on strange header files, but otherwise works
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "guts.h" | |
#include "png.h" | |
#include "mem.h" | |
static unsigned int flip(unsigned int x) | |
{ | |
x = ((x & 0x0000ffff) << 16) | ((x & 0xffff0000) >> 16); | |
x = ((x & 0x00ff00ff) << 8) | ((x & 0xff00ff00) >> 8); | |
x = ((x & 0x0f0f0f0f) << 4) | ((x & 0xf0f0f0f0) >> 4); | |
x = ((x & 0x33333333) << 2) | ((x & 0xcccccccc) >> 2); | |
x = ((x & 0x55555555) << 1) | ((x & 0xaaaaaaaa) >> 1); | |
return x; | |
} | |
static unsigned int swap(unsigned int x) | |
{ | |
x = ((x&0x0000ffff)<<16)|((x&0xffff0000)>>16); | |
x = ((x&0x00ff00ff)<<8)|((x&0xff00ff00)>>8); | |
return x; | |
} | |
png_dismembered png_dismember(const unsigned char* png, int maxsize) { | |
const unsigned char* end = png+maxsize; | |
png_dismembered out = {0,0,0,0,0,0}, err_invalid = {LPNG_ERR_INVALID_PNG}, err_unsupported = {LPNG_ERR_UNSUPPORTED_DATA}; | |
if (*(unsigned int*)png!=0x474e5089) return err_invalid; | |
png += 12; | |
while (*(unsigned int*)png!=0x444e4549 && png<end) { | |
int chunksz = swap(*(unsigned int*)(png-4)); | |
if (*(unsigned int*)png==0x54414449) { | |
if (!out.data) out.data = mem_alloc(chunksz); | |
else out.data = mem_resize(out.data,out.datasize+chunksz); | |
mem_copy(png+4,out.data+out.datasize,chunksz); | |
out.datasize += chunksz; | |
} else if (*(int*)png==0x52444849) { | |
out.width = swap(*(unsigned int*)(png+4)); | |
out.height = swap(*(unsigned int*)(png+8)); | |
if (png[12] != 8) | |
return err_unsupported; | |
out.channels = png[13]; | |
switch (out.channels) { | |
case 2: | |
out.channels = 3; | |
break; | |
case 6: | |
out.channels = 4; | |
break; | |
default: | |
return err_unsupported; | |
} | |
} | |
png += chunksz+12; | |
} | |
if (png>=end||out.width==0||out.height==0) return err_invalid; | |
return out; | |
} | |
static const unsigned char hufforder[] = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; | |
static const unsigned char len_starts[] = {0,11,19,35,67,131}; | |
static const unsigned short dst_starts[] = {0,5,9,17,33,65,129,257,513,1025,2049,4097,8193,16385}; | |
static unsigned char paeth(unsigned char a, unsigned char b, unsigned char c) | |
{ | |
unsigned char p = a + b - c; | |
unsigned char pa = p-a, pb = p-b, pc = p-c; | |
if (pa<0) pa=-pa; | |
if (pb<0) pb=-pb; | |
if (pc<0) pc=-pc; | |
if (pa <= pb && pa <= pc) return a; | |
else if (pb <= pc) return b; | |
else return c; | |
} | |
unsigned char* png_dismembered_parse(png_dismembered data) | |
{ | |
unsigned char *err_invalid = (unsigned char*)LPNG_ERR_INVALID_PNG, *err_unsupported = (unsigned char*)LPNG_ERR_UNSUPPORTED_DATA; | |
unsigned char* idat = data.data; | |
unsigned int width = data.width, height = data.height, chn = data.channels; | |
unsigned char* buf = mem_alloc(((width*=chn)+1)*height); | |
unsigned int v, i=0, b=0, bf=0, c=0, tex=0; | |
idat+=2; | |
do { | |
bf = (*(unsigned int*)idat>>b); b+=3; | |
if ((bf&6)==2) do { | |
if (b >= 16) v = flip((*(unsigned int*)(idat+=2))>>(b-=16)); | |
else v = flip((*(unsigned int*)idat)>>b); | |
v>>=23; b+=9; | |
if (v>=0x190 && v<=0x1ff) v -= 0x100; | |
else { | |
v>>=1; b--; | |
if (v>=0x30 && v<=0xbf) v -= 0x30; | |
else if (v>=0xc0 && v<=0xc7) v += 0x58; | |
else { | |
v>>=1; b--; | |
if (v <= 0x17) v += 0x100; | |
else | |
return err_invalid; | |
} | |
} | |
if (v < 256) buf[i++] = v; | |
else if (v > 256) | |
return err_unsupported; | |
} while (v != 256); | |
else if ((bf&6)==4) { | |
int hclen, hdist, hlit, j = 0; | |
unsigned char hcls[19]; | |
unsigned char hcl_codes[19]; | |
unsigned char code_count[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; | |
unsigned char hcl_ncode[8]; | |
unsigned short dict[318]; | |
unsigned short dict_ncode[16]; | |
unsigned short dict_codes[318]; | |
if (b >= 16) v = (*(unsigned int*)(idat+=2)>>(b-=16)); | |
else v = ((*(unsigned int*)idat)>>b); | |
hlit = (v & 31) + 257; | |
hdist = ((v >> 5) & 31) + 1; | |
hclen = ((v >> 10) & 15) + 4; | |
b += 14; | |
while (j < hclen) { | |
if (b >= 16) v = (*(unsigned int*)(idat+=2)>>(b-=16)); | |
else v = (*(unsigned int*)idat>>b); | |
code_count[hcls[hufforder[j]] = (v & 7)]++; | |
b+=3; | |
j++; | |
} | |
while (j < 19) hcls[hufforder[j++]] = 0; | |
code_count[0] = 0; | |
for (j = 1, c = 0; j <= 7; j++) | |
c = hcl_ncode[j] = (c+(code_count[j-1]))<<1; | |
for (j = 0; j < 19; j++) | |
if (hcls[j]) | |
hcl_codes[j] = (flip(hcl_ncode[hcls[j]]++)>>(32-hcls[j])); | |
for (j = 0; j < hlit+hdist; j++) { | |
if (b >= 16) v = ((*(unsigned int*)(idat+=2))>>(b-=16)); | |
else v = ((*(unsigned int*)idat)>>b); | |
if ((unsigned int)(idat-data.data) > data.datasize) | |
return err_invalid; | |
for (c = 0; c < 20; c++) | |
if (hcls[c] && (v&((1<<hcls[c])-1))==hcl_codes[c]) { | |
b+=hcls[c]; | |
if (c < 16) dict[j] = c; | |
else if (c==16) { | |
unsigned int rep = 3+(((*(unsigned int*)idat)>>b)&3); | |
if (!j) return err_invalid; | |
for (c = 0; c < rep; c++) | |
dict[j+c] = dict[j-1]; | |
j+=c-1; | |
b+=2; | |
} else if (c==17) { | |
unsigned int rep = 3+(((*(unsigned int*)idat)>>b)&7); | |
for (c = 0; c < rep; c++) | |
dict[j+c] = 0; | |
j+=c-1; | |
b+=3; | |
} else if (c==18) { | |
unsigned int rep = 11+(((*(unsigned int*)idat)>>b)&127); | |
for (c = 0; c < rep; c++) | |
dict[j+c] = 0; | |
j+=c-1; | |
b+=7; | |
} else | |
return err_unsupported; | |
break; | |
} else if (c==19) | |
return err_invalid; | |
} | |
if (dict[256]==0) | |
return err_invalid; | |
hclen = hlit; | |
do { | |
int joff = 0; | |
if (hclen==hdist) joff=hlit; | |
for (j = 0; j < 16; j++) code_count[j] = 0; | |
for (j = 0; j < hclen; j++) | |
code_count[dict[j+joff]]++; | |
code_count[0] = 0; | |
for (j = 1, c = 0; j < 16; j++) | |
c = dict_ncode[j] = (c+(code_count[j-1]))<<1; | |
for (j = 0; j < hclen; j++) | |
dict_codes[j+joff] = (flip(dict_ncode[dict[j+joff]]++)>>(32-dict[j+joff])); | |
if (hclen == hlit) hclen = hdist; | |
else hclen = 0; | |
} while (hclen); | |
do { | |
if (b >= 16) v = ((*(unsigned int*)(idat+=2))>>(b-=16)); | |
else v = ((*(unsigned int*)idat)>>b); | |
for (c = 0; c < (unsigned int)hlit; c++) | |
if (dict[c] && (v&((1<<dict[c])-1))==dict_codes[c]) { | |
b+=dict[c]; | |
if (i>174240) | |
i=i; | |
if (c < 256) buf[i++] = c; | |
else if (c>256) { | |
int len, dst; | |
if (c < 265) len = c-254; | |
else if (c == 285) len = 258; | |
else { | |
int bits; | |
if (b >= 16) v = ((*(unsigned int*)(idat+=2))>>(b-=16)); | |
else v = ((*(unsigned int*)idat)>>b); | |
bits = (c-261)/4; | |
len = (v&((1<<bits)-1))+len_starts[bits]+((c-261)%4)*(1<<bits); | |
b += bits; | |
} | |
if (b >= 16) v = ((*(unsigned int*)(idat+=2))>>(b-=16)); | |
else v = ((*(unsigned int*)idat)>>b); | |
for (c = hlit; c < (unsigned int)(hlit+hdist); c++) | |
if (dict[c] && (v&((1<<dict[c])-1))==dict_codes[c]) { | |
b+=dict[c]; | |
c -= hlit; | |
if (c < 4) dst = c+1; | |
else { | |
int bits; | |
if (b >= 16) v = ((*(unsigned int*)(idat+=2))>>(b-=16)); | |
else v = ((*(unsigned int*)idat)>>b); | |
bits = (c/2)-1; | |
dst = (v&((1<<bits)-1))+dst_starts[bits]+(c&1)*(1<<bits); | |
b += bits; | |
} | |
break; | |
} | |
if (c==hlit+hdist) | |
return err_invalid; | |
for (j = i-dst; len; len--) | |
buf[i++] = buf[j++]; | |
} | |
break; | |
} | |
if (c==hlit) | |
return err_invalid; | |
} while (c != 256); | |
} else if ((bf&6)==0) { | |
b=0; idat++; | |
c = *(unsigned short*)idat; | |
idat+=4; | |
while (c) { | |
buf[i++] = *(idat++); | |
c--; | |
} | |
} else | |
return err_unsupported; | |
} while ((bf&1)==0); | |
for (i = 0; i < height; i++) { | |
v = buf[bf = i*(width+1)]; | |
for (b = 0; b < width; b++) switch (v) { | |
case 0: | |
buf[i*width+b] = buf[bf+b+1]; | |
break; | |
case 1: | |
if (b < chn) buf[i*width+b] = buf[bf+b+1]; | |
else buf[i*width+b] = buf[i*width+b-chn] + buf[bf+b+1]; | |
break; | |
case 2: | |
buf[i*width+b] = (i?buf[(i-1)*width+b]:0) + buf[bf+b+1]; | |
break; | |
case 3: | |
buf[i*width+b] = ((i?buf[(i-1)*width+b]:0)+(b<chn?0:buf[i*width+b-chn]))/2+buf[bf+b+1]; | |
break; | |
case 4: | |
buf[i*width+b] = paeth( | |
(b<chn?0:buf[i*width+b-chn]), | |
(i?buf[(i-1)*width+b]:0), | |
(b<chn?0:(i?buf[(i-1)*width+b-chn]:0)) | |
)+buf[bf+b+1]; | |
break; | |
default: | |
return err_unsupported; | |
break; | |
} | |
}//*/ | |
return buf; | |
} | |
unsigned char* png_parse(const unsigned char* bytes, int maxsize, int* whc) | |
{ | |
png_dismembered png = png_dismember(bytes, maxsize); | |
unsigned char* out = png_dismembered_parse(png); | |
mem_dealloc(png.data); | |
whc[0] = png.width; | |
whc[1] = png.height; | |
whc[2] = png.channels; | |
return out; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment