(mpng_decode.c) Style nits

This commit is contained in:
twinaphex 2015-02-19 02:46:41 +01:00
parent 41403fb3d6
commit 6bb81b59d5
1 changed files with 385 additions and 269 deletions

View File

@ -15,7 +15,11 @@ static void tinfl_deinit(tinfl_decompressor* r) {}
typedef z_stream tinfl_decompressor; typedef z_stream tinfl_decompressor;
typedef int tinfl_status; typedef int tinfl_status;
static uint32_t mz_crc32(uint32_t crc, const uint8_t* buf, size_t len) { return crc32(crc, buf, len); } static uint32_t mz_crc32(uint32_t crc,
const uint8_t* buf, size_t len)
{
return crc32(crc, buf, len);
}
enum { enum {
TINFL_FLAG_PARSE_ZLIB_HEADER = 1, TINFL_FLAG_PARSE_ZLIB_HEADER = 1,
@ -32,7 +36,8 @@ static void tinfl_init(tinfl_decompressor* r)
inflateInit(r); inflateInit(r);
} }
static tinfl_status tinfl_decompress(tinfl_decompressor* r, const uint8_t * pIn_buf_next, size_t* pIn_buf_size, static tinfl_status tinfl_decompress(tinfl_decompressor* r,
const uint8_t * pIn_buf_next, size_t* pIn_buf_size,
uint8_t * pOut_buf_start, uint8_t * pOut_buf_next, size_t* pOut_buf_size, uint8_t * pOut_buf_start, uint8_t * pOut_buf_next, size_t* pOut_buf_size,
uint32_t decomp_flags) uint32_t decomp_flags)
{ {
@ -40,7 +45,8 @@ static tinfl_status tinfl_decompress(tinfl_decompressor* r, const uint8_t * pIn_
r->avail_in = *pIn_buf_size; r->avail_in = *pIn_buf_size;
r->next_out = pOut_buf_next; r->next_out = pOut_buf_next;
r->avail_out = *pOut_buf_size; r->avail_out = *pOut_buf_size;
return inflate(r, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? Z_NO_FLUSH : Z_SYNC_FLUSH); return inflate(r, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT)
? Z_NO_FLUSH : Z_SYNC_FLUSH);
} }
static void tinfl_deinit(tinfl_decompressor* r) static void tinfl_deinit(tinfl_decompressor* r)
@ -49,7 +55,11 @@ static void tinfl_deinit(tinfl_decompressor* r)
} }
#endif #endif
uint32_t read8r(const uint8_t* source) { return *source; } uint32_t read8r(const uint8_t* source)
{
return *source;
}
uint32_t read24r(const uint8_t* source) uint32_t read24r(const uint8_t* source)
{ {
return ((source[0] << 16) | (source[1] << 8) | (source[2] << 0)); return ((source[0] << 16) | (source[1] << 8) | (source[2] << 0));
@ -65,25 +75,11 @@ uint32_t read32r(const uint8_t* source)
bool png_decode(const void * pngdata, size_t pnglen, struct mpng_image * img, enum video_format format) bool png_decode(const void * pngdata, size_t pnglen, struct mpng_image * img, enum video_format format)
{ {
tinfl_decompressor inflator;
unsigned i; unsigned i;
unsigned b, x, y; unsigned b, x, y;
memset(img, 0, sizeof(struct mpng_image));
if (format!=FMT_RGB888 && format!=FMT_XRGB8888 && format!=FMT_ARGB8888) return false;
if (pnglen<8) return false;
const uint8_t * data=(const uint8_t*)pngdata;
if (memcmp(data, "\x89PNG\r\n\x1A\n", 8)) return false;
const uint8_t * dataend=data+pnglen;
data+=8;
unsigned int width;
unsigned int height;
uint8_t * pixels=NULL;
uint8_t * pixelsat;
uint8_t * pixelsend;
//chop off some warnings... these are all initialized in IHDR
unsigned int bitsperchannel; unsigned int bitsperchannel;
unsigned int colortype; unsigned int colortype;
unsigned int compressiontype; unsigned int compressiontype;
@ -91,26 +87,70 @@ bool png_decode(const void * pngdata, size_t pnglen, struct mpng_image * img, en
unsigned int interlacetype; unsigned int interlacetype;
unsigned int bpl; unsigned int bpl;
unsigned int width;
unsigned int height;
uint8_t * pixelsat;
uint8_t * pixelsend;
unsigned int palette[256]; unsigned int palette[256];
memset(palette, 0, sizeof(palette));//not gonna catch palette overflows
int palettelen = 0; int palettelen = 0;
tinfl_decompressor inflator; const uint8_t *data = NULL;
const uint8_t *dataend = NULL;
uint8_t * pixels = NULL;
memset(img, 0, sizeof(struct mpng_image));
if (format!=FMT_RGB888 && format!=FMT_XRGB8888 && format!=FMT_ARGB8888)
return false;
if (pnglen<8)
return false;
data = (const uint8_t*)pngdata;
if (!data)
return false;
if (memcmp(data, "\x89PNG\r\n\x1A\n", 8))
return false;
dataend = data + pnglen;
data += 8;
/* not gonna catch palette overflows */
memset(palette, 0, sizeof(palette));
tinfl_init(&inflator); tinfl_init(&inflator);
while (true) while (true)
{ {
if (data+4+4>dataend) goto bad; unsigned int chunklen;
unsigned int chunklen=read32r(data); unsigned int chunktype;
unsigned int chunktype=read32r(data+4); unsigned int chunkchecksum;
if (chunklen>=0x80000000) goto bad; unsigned int actualchunkchecksum;
if (data+4+chunklen+4>dataend) goto bad; const uint8_t * chunkdata = NULL;
unsigned int chunkchecksum=mz_crc32(mz_crc32(0, NULL, 0), (uint8_t*)data+4, 4+chunklen);
const uint8_t * chunkdata=data+4+4; if (data + 4 + 4 > dataend)
unsigned int actualchunkchecksum=read32r(data+4+4+chunklen); goto bad;
if (actualchunkchecksum!=chunkchecksum) goto bad;
chunklen = read32r(data);
chunktype = read32r(data + 4);
if (chunklen >= 0x80000000)
goto bad;
if (data + 4 + chunklen + 4 > dataend)
goto bad;
chunkchecksum = mz_crc32(mz_crc32(0, NULL, 0), (uint8_t*)data+4, 4+chunklen);
chunkdata = data+4+4;
actualchunkchecksum = read32r(data+4+4+chunklen);
if (actualchunkchecksum != chunkchecksum)
goto bad;
data += 4 + 4 + chunklen + 4; data += 4 + 4 + chunklen + 4;
switch (chunktype) switch (chunktype)
{ {
case 0x49484452: //IHDR case 0x49484452: //IHDR
@ -123,38 +163,64 @@ bool png_decode(const void * pngdata, size_t pnglen, struct mpng_image * img, en
read8(filtertype); read8(filtertype);
read8(interlacetype); read8(interlacetype);
if (width>=0x80000000) goto bad; if (width>=0x80000000)
if (width==0) goto bad; goto bad;
if (height>=0x80000000) goto bad; if (width==0)
if (height==0) goto bad; goto bad;
if (colortype!=2 && colortype!=3 && colortype!=6) goto bad; if (height>=0x80000000)
goto bad;
if (height==0)
goto bad;
if (colortype!=2 && colortype!=3 && colortype!=6)
goto bad;
//Greyscale 0 //Greyscale 0
//Truecolour 2 //Truecolour 2
//Indexed-colour 3 //Indexed-colour 3
//Greyscale with alpha 4 //Greyscale with alpha 4
//Truecolour with alpha 6 //Truecolour with alpha 6
if (colortype==2 && bitsperchannel!=8) goto bad;//truecolor; can be 16bpp but I don't want that. if (colortype==2 && bitsperchannel!=8)
if (colortype==3 && bitsperchannel!=1 && bitsperchannel!=2 && bitsperchannel!=4 && bitsperchannel!=8) goto bad;//paletted goto bad;//truecolor; can be 16bpp but I don't want that.
if (colortype==6 && bitsperchannel!=8) goto bad;//truecolor with alpha if (colortype==3 && bitsperchannel!=1 && bitsperchannel!=2 && bitsperchannel!=4 && bitsperchannel!=8)
if (colortype==6 && format!=FMT_ARGB8888) goto bad;//can only decode alpha on ARGB formats goto bad;//paletted
if (compressiontype!=0) goto bad; if (colortype==6 && bitsperchannel!=8)
if (filtertype!=0) goto bad; goto bad;//truecolor with alpha
if (interlacetype!=0 && interlacetype!=1) goto bad; if (colortype==6 && format!=FMT_ARGB8888)
goto bad;//can only decode alpha on ARGB formats
if (compressiontype!=0)
goto bad;
if (filtertype!=0)
goto bad;
if (interlacetype!=0 && interlacetype!=1)
goto bad;
if (colortype==2)
bpl=3*width;
if (colortype==3)
bpl=(width*bitsperchannel + bitsperchannel-1)/8;
if (colortype==6)
bpl=4*width;
if (colortype==2) bpl=3*width;
if (colortype==3) bpl=(width*bitsperchannel + bitsperchannel-1)/8;
if (colortype==6) bpl=4*width;
pixels = (uint8_t*)malloc((bpl+1)*height); if (!pixels) goto bad; pixels = (uint8_t*)malloc((bpl+1)*height); if (!pixels) goto bad;
if (!pixels)
goto bad;
pixelsat = pixels; pixelsat = pixels;
pixelsend = pixels+(bpl+1)*height; pixelsend = pixels+(bpl+1)*height;
} }
break; break;
case 0x504c5445: //PLTE case 0x504c5445: //PLTE
{ {
if (pixels==NULL || palettelen!=0) goto bad; if (pixels == NULL || palettelen!=0)
if (chunklen==0 || chunklen%3 || chunklen>3*256) goto bad; goto bad;
if (colortype!=3) break;//palette on rgb is allowed but rare, and it's just a recommendation anyways. if (chunklen == 0 || chunklen%3 || chunklen>3*256)
goto bad;
if (colortype!=3)
break;//palette on rgb is allowed but rare, and it's just a recommendation anyways.
palettelen = chunklen/3; palettelen = chunklen/3;
for (i=0;i<palettelen;i++) for (i=0;i<palettelen;i++)
{ {
read24(palette[i]); read24(palette[i]);
@ -164,10 +230,12 @@ bool png_decode(const void * pngdata, size_t pnglen, struct mpng_image * img, en
break; break;
case 0x74524E53: //tRNS case 0x74524E53: //tRNS
{ {
if (format!=FMT_ARGB8888 || pixels==NULL || pixels!=pixelsat) goto bad; if (format!=FMT_ARGB8888 || pixels == NULL || pixels!=pixelsat)
goto bad;
if (colortype == 2) if (colortype == 2)
{ {
if (palettelen==0) goto bad; if (palettelen == 0)
goto bad;
goto bad; goto bad;
} }
else if (colortype == 3) else if (colortype == 3)
@ -179,44 +247,77 @@ goto bad;
break; break;
case 0x49444154: //IDAT case 0x49444154: //IDAT
{ {
if (pixels==NULL || (colortype==3 && palettelen==0)) goto bad; size_t chunklencopy, byteshere;
size_t chunklencopy=chunklen; tinfl_status status;
size_t byteshere=(pixelsend-pixelsat)+1;
tinfl_status status=tinfl_decompress(&inflator, (const uint8_t*)chunkdata, &chunklencopy, pixels, pixelsat, &byteshere, if (pixels == NULL || (colortype == 3 && palettelen == 0))
TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | TINFL_FLAG_PARSE_ZLIB_HEADER); goto bad;
chunklencopy = chunklen;
byteshere = (pixelsend-pixelsat)+1;
status = tinfl_decompress(&inflator, (const uint8_t*)chunkdata,
&chunklencopy, pixels, pixelsat, &byteshere,
TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF
| TINFL_FLAG_PARSE_ZLIB_HEADER);
pixelsat += byteshere; pixelsat += byteshere;
if (status<0) goto bad;
if (status<0)
goto bad;
} }
break; break;
case 0x49454e44: //IEND case 0x49454e44: //IEND
{ {
if (data!=dataend) goto bad; size_t zero, finalbytes;
if (chunklen) goto bad; tinfl_status status;
size_t zero=0; unsigned int bpppacked;
size_t finalbytes=(pixelsend-pixelsat); uint8_t * out = NULL;
tinfl_status status=tinfl_decompress(&inflator, (const uint8_t*)NULL, &zero, pixels, pixelsat, &finalbytes, uint8_t *filteredline = NULL;
uint8_t *prevout = NULL;
if (data!=dataend)
goto bad;
if (chunklen)
goto bad;
zero = 0;
finalbytes = (pixelsend-pixelsat);
status = tinfl_decompress(&inflator,
(const uint8_t*)NULL, &zero, pixels, pixelsat, &finalbytes,
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | TINFL_FLAG_PARSE_ZLIB_HEADER); TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | TINFL_FLAG_PARSE_ZLIB_HEADER);
pixelsat += finalbytes; pixelsat += finalbytes;
if (status!=TINFL_STATUS_DONE) goto bad;
if (pixelsat!=pixelsend) goto bad;//too little data (can't be too much because we didn't give it that buffer size) if (status!=TINFL_STATUS_DONE)
uint8_t * out=(uint8_t*)malloc(videofmt_byte_per_pixel(format)*width*height); goto bad;
if (pixelsat!=pixelsend)
goto bad;//too little data (can't be too much because we didn't give it that buffer size)
out = (uint8_t*)malloc(videofmt_byte_per_pixel(format)*width*height);
if (!out)
goto bad;
//TODO: deinterlace at random point //TODO: deinterlace at random point
//run filters //run filters
unsigned int bpppacked=((colortype==2)?3:(colortype==6)?4:1); bpppacked = ((colortype == 2) ? 3 : (colortype == 6) ? 4 : 1);
uint8_t * prevout=out+(4*width*1); prevout = out + (4 * width * 1);
if (height==1)
{ if (!prevout)
prevout=out; goto bad;
//this will blow up if a 1px high image is filtered with Paeth, but who the hell would do that? //this will blow up if a 1px high image is filtered with Paeth, but who the hell would do that?
} if (height == 1)
prevout=out;
memset(prevout, 0, 4*width*1);//not using bpp here because we only need a chunk of black anyways memset(prevout, 0, 4*width*1);//not using bpp here because we only need a chunk of black anyways
uint8_t * filteredline=pixels; filteredline = pixels;
if (!filteredline)
goto bad;
for (y=0;y<height;y++) for (y=0;y<height;y++)
{ {
uint8_t *thisout = out + (bpl*y); uint8_t *thisout = out + (bpl*y);
switch (*(filteredline++)) switch (*(filteredline++))
{ {
case 0: case 0:
@ -261,9 +362,12 @@ goto bad;
int pb = abs(p - b); int pb = abs(p - b);
int pc = abs(p - c); int pc = abs(p - c);
if (pa<=pb && pa<=pc) prediction=a; if (pa <= pb && pa <= pc)
else if (pb<=pc) prediction=b; prediction = a;
else prediction=c; else if (pb <= pc)
prediction = b;
else
prediction = c;
thisout[x] = filteredline[x] + prediction; thisout[x] = filteredline[x] + prediction;
} }
@ -280,14 +384,18 @@ goto bad;
int pb = abs(p - b); int pb = abs(p - b);
int pc = abs(p - c); int pc = abs(p - c);
if (pa<=pb && pa<=pc) prediction=a; if (pa <= pb && pa <= pc)
else if (pb<=pc) prediction=b; prediction = a;
else prediction=c; else if (pb <= pc)
prediction = b;
else
prediction = c;
thisout[x] = filteredline[x] + prediction; thisout[x] = filteredline[x] + prediction;
} }
break; break;
default: goto bad; default:
goto bad;
} }
prevout=thisout; prevout=thisout;
filteredline+=bpl; filteredline+=bpl;
@ -303,11 +411,14 @@ goto bad;
{ {
int y = height; int y = height;
uint8_t *outp = out + 3 * width * height; uint8_t *outp = out + 3 * width * height;
do {
uint8_t * inp=out+y*bpl;
do
{
uint8_t *inp = out + y * bpl;
int x = (width+7) / 8; int x = (width+7) / 8;
do {
do
{
x--; x--;
inp--; inp--;
for (b = 0; b < 8; b++) for (b = 0; b < 8; b++)
@ -326,10 +437,13 @@ goto bad;
{ {
int y=height; int y=height;
uint8_t * outp=out+3*width*height; uint8_t * outp=out+3*width*height;
do { do
{
int x;
unsigned char *inp = out + y * bpl; unsigned char *inp = out + y * bpl;
int x=(width+3)/4; x =(width + 3) / 4;
do { do {
x--; x--;
inp--; inp--;
@ -412,7 +526,8 @@ goto bad;
} }
break; break;
default: default:
if (!(chunktype&0x20000000)) goto bad;//unknown critical if (!(chunktype&0x20000000))
goto bad;//unknown critical
//otherwise ignore //otherwise ignore
} }
} }
@ -421,5 +536,6 @@ bad:
tinfl_deinit(&inflator); tinfl_deinit(&inflator);
free(pixels); free(pixels);
memset(img, 0, sizeof(struct mpng_image)); memset(img, 0, sizeof(struct mpng_image));
return false; return false;
} }