Skip to content

Instantly share code, notes, and snippets.

@superllama
Last active February 25, 2020 00:35
Show Gist options
  • Save superllama/8b2c16dda9d807c57940026a9c061975 to your computer and use it in GitHub Desktop.
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
#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