mirror of
https://github.com/libretro/RetroArch
synced 2025-04-01 22:20:31 +00:00
Update mpng_decode.c
This commit is contained in:
parent
9e6d0004fd
commit
2697bb2230
@ -1,435 +1,321 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <boolean.h>
|
||||
|
||||
#ifdef WANT_MINIZ
|
||||
#define MINIZ_HEADER_FILE_ONLY
|
||||
#include "miniz.c"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <formats/mpng.h>
|
||||
|
||||
static uint32_t dword_be(const uint8_t *buf)
|
||||
#ifndef HAVE_ZLIB
|
||||
# define MINIZ_HEADER_FILE_ONLY
|
||||
# include "miniz.c"
|
||||
static void tinfl_deinit(tinfl_decompressor* r) {}
|
||||
|
||||
#else
|
||||
|
||||
# include <zlib.h>
|
||||
typedef z_stream tinfl_decompressor;
|
||||
typedef int tinfl_status;
|
||||
|
||||
static uint32_t mz_crc32(uint32_t crc, const uint8_t* buf, size_t len) { return crc32(crc, buf, len); }
|
||||
|
||||
enum {
|
||||
TINFL_FLAG_PARSE_ZLIB_HEADER = 1,
|
||||
TINFL_FLAG_HAS_MORE_INPUT = 2,
|
||||
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4,
|
||||
TINFL_FLAG_COMPUTE_ADLER32 = 8,
|
||||
|
||||
TINFL_STATUS_DONE = Z_STREAM_END
|
||||
};
|
||||
|
||||
static void tinfl_init(tinfl_decompressor* r)
|
||||
{
|
||||
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3] << 0);
|
||||
memset(r, 0, sizeof(*r));
|
||||
inflateInit(r);
|
||||
}
|
||||
|
||||
static uint16_t word_be(const uint8_t *buf)
|
||||
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,
|
||||
uint32_t decomp_flags)
|
||||
{
|
||||
return (buf[0] << 16) | (buf[1] << 8) | (buf[2] << 0);
|
||||
r->next_in = (Bytef*)pIn_buf_next;
|
||||
r->avail_in = *pIn_buf_size;
|
||||
r->next_out = pOut_buf_next;
|
||||
r->avail_out = *pOut_buf_size;
|
||||
return inflate(r, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? Z_NO_FLUSH : Z_SYNC_FLUSH);
|
||||
}
|
||||
|
||||
enum mpng_chunk_type
|
||||
static void tinfl_deinit(tinfl_decompressor* r)
|
||||
{
|
||||
MPNG_CHUNK_TRNS = 0x74524E53,
|
||||
MPNG_CHUNK_IHDR = 0x49484452,
|
||||
MPNG_CHUNK_IDAT = 0x49444154,
|
||||
MPNG_CHUNK_PLTE = 0x504c5445,
|
||||
MPNG_CHUNK_IEND = 0x49454e44,
|
||||
};
|
||||
inflateEnd(r);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct mpng_ihdr
|
||||
uint32_t read8r(const uint8_t* source) { return *source; }
|
||||
uint32_t read24r(const uint8_t* source)
|
||||
{
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint8_t depth;
|
||||
uint8_t color_type;
|
||||
uint8_t compression;
|
||||
uint8_t filter;
|
||||
uint8_t interlace;
|
||||
};
|
||||
|
||||
static const uint8_t mpng_magic[8] = {
|
||||
0x89, 'P', 'N', 'G', 0x0d, 0x0a, 0x1a, 0x0a,
|
||||
};
|
||||
|
||||
struct mpng_chunk
|
||||
return ((source[0]<<16) | (source[1]<<8) | (source[2]<<0));
|
||||
}
|
||||
uint32_t read32r(const uint8_t* source)
|
||||
{
|
||||
uint32_t size;
|
||||
uint32_t type;
|
||||
const uint8_t *data;
|
||||
};
|
||||
|
||||
bool mpng_parse_ihdr(struct mpng_ihdr *ihdr, struct mpng_chunk *chunk,
|
||||
enum video_format format, unsigned int *bpl,
|
||||
uint8_t *pixels, uint8_t *pixelsat, uint8_t *pixelsend)
|
||||
{
|
||||
ihdr->width = dword_be(chunk->data + 0);
|
||||
ihdr->height = dword_be(chunk->data + 4);
|
||||
ihdr->depth = chunk->data[8];
|
||||
ihdr->color_type = chunk->data[9];
|
||||
ihdr->compression = chunk->data[10];
|
||||
ihdr->filter = chunk->data[11];
|
||||
ihdr->interlace = chunk->data[12];
|
||||
|
||||
if (ihdr->width == 0 || ihdr->height == 0)
|
||||
return false;
|
||||
|
||||
if (ihdr->width >= 0x80000000)
|
||||
return false;
|
||||
|
||||
if (ihdr->height >= 0x80000000)
|
||||
return false;
|
||||
|
||||
if ( ihdr->color_type != 2
|
||||
&& ihdr->color_type != 3
|
||||
&& ihdr->color_type != 6)
|
||||
return false;
|
||||
|
||||
|
||||
if (ihdr->compression != 0)
|
||||
return false;
|
||||
if (ihdr->filter != 0)
|
||||
return false;
|
||||
if (ihdr->interlace != 0 && ihdr->interlace != 1)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Greyscale 0
|
||||
* Truecolour 2
|
||||
* Indexed-colour 3
|
||||
* Greyscale with alpha 4
|
||||
* Truecolour with alpha 6
|
||||
**/
|
||||
|
||||
switch (ihdr->color_type)
|
||||
{
|
||||
case 2:
|
||||
/* Truecolor; can be 16bpp but I don't want that. */
|
||||
if (ihdr->depth != 8)
|
||||
return false;
|
||||
*bpl = 3 * ihdr->width;
|
||||
break;
|
||||
case 3:
|
||||
/* Paletted. */
|
||||
if (ihdr->depth != 1
|
||||
&& ihdr->depth != 2
|
||||
&& ihdr->depth != 4
|
||||
&& ihdr->depth != 8)
|
||||
return false;
|
||||
*bpl = (ihdr->width * ihdr->depth + ihdr->depth - 1) / 8;
|
||||
break;
|
||||
case 6:
|
||||
/* Truecolor with alpha. */
|
||||
if (ihdr->depth != 8)
|
||||
return false;
|
||||
|
||||
/* Can only decode alpha on ARGB formats. */
|
||||
if (format != FMT_ARGB8888)
|
||||
return false;
|
||||
*bpl = 4 * ihdr->width;
|
||||
break;
|
||||
}
|
||||
|
||||
pixels = (uint8_t*)malloc((*bpl + 1) * ihdr->height);
|
||||
|
||||
if (!pixels)
|
||||
return false;
|
||||
|
||||
pixelsat = (uint8_t*)pixels;
|
||||
pixelsend = (uint8_t*)(pixels + (*bpl + 1) * ihdr->height);
|
||||
|
||||
return true;
|
||||
return ((source[0]<<24) | (source[1]<<16) | (source[2]<<8) | (source[3]<<0));
|
||||
}
|
||||
|
||||
static bool mpng_read_plte(struct mpng_ihdr *ihdr,
|
||||
struct mpng_chunk *chunk,
|
||||
uint8_t *pixels,
|
||||
uint32_t *buffer, unsigned entries)
|
||||
#define read8(target) do { target=read8r(chunkdata); chunkdata++; } while(0)
|
||||
#define read24(target) do { target=read24r(chunkdata); chunkdata+=3; } while(0)
|
||||
#define read32(target) do { target=read32r(chunkdata); chunkdata+=4; } while(0)
|
||||
|
||||
bool png_decode(const void * pngdata, size_t pnglen, struct mpng_image * img, enum video_format format)
|
||||
{
|
||||
unsigned i;
|
||||
if (chunk->size % 3)
|
||||
return false;
|
||||
if (!pixels || entries != 0)
|
||||
return false;
|
||||
if (chunk->size == 0 || chunk->size > 3 * 256)
|
||||
return false;
|
||||
|
||||
/* Palette on RGB is allowed but rare,
|
||||
* and it's just a recommendation anyways. */
|
||||
if (ihdr->color_type != 3)
|
||||
return true; /* not sure about this - Alcaro review? */
|
||||
|
||||
for (i = 0; i < entries; i++)
|
||||
{
|
||||
uint32_t r = chunk->data[3 * i + 0];
|
||||
uint32_t g = chunk->data[3 * i + 1];
|
||||
uint32_t b = chunk->data[3 * i + 2];
|
||||
buffer[i] = (r << 16) | (g << 8) | (b << 0) | (0xffu << 24);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool png_decode_iterate(const uint8_t *data, const uint8_t *data_end,
|
||||
struct mpng_ihdr *ihdr, struct mpng_image *img,
|
||||
uint32_t *palette, enum video_format format,
|
||||
unsigned int *bpl, int *palette_len, uint8_t *pixels,
|
||||
uint8_t *pixelsat, uint8_t *pixelsend
|
||||
#ifdef WANT_MINIZ
|
||||
,tinfl_decompressor *inflator
|
||||
#endif
|
||||
)
|
||||
{
|
||||
unsigned int chunkchecksum;
|
||||
unsigned int actualchunkchecksum;
|
||||
struct mpng_chunk chunk = {0};
|
||||
|
||||
if ((data + 4 + 4) > data_end)
|
||||
return -1;
|
||||
|
||||
chunk.size = dword_be(data);
|
||||
chunk.type = dword_be(data + 4);
|
||||
|
||||
if (chunk.size >= 0x80000000)
|
||||
return -1;
|
||||
if ((data + 4 + chunk.size + 4) > data_end)
|
||||
return -1;
|
||||
|
||||
chunkchecksum = mz_crc32(mz_crc32(0, NULL, 0), (uint8_t*)data+4, 4 + chunk.size);
|
||||
chunk.data = (const uint8_t*)(data + 4 + 4);
|
||||
actualchunkchecksum = dword_be(data + 4 + 4 + chunk.size);
|
||||
|
||||
if (actualchunkchecksum != chunkchecksum)
|
||||
return -1;
|
||||
|
||||
data += 4 + 4 + chunk.size + 4;
|
||||
|
||||
switch (chunk.type)
|
||||
{
|
||||
case MPNG_CHUNK_IHDR:
|
||||
if (!mpng_parse_ihdr(ihdr, &chunk, format, bpl, pixels,
|
||||
pixelsat, pixelsend))
|
||||
return -1;
|
||||
break;
|
||||
case MPNG_CHUNK_PLTE:
|
||||
*palette_len = chunk.size / 3;
|
||||
if (!mpng_read_plte(ihdr, &chunk, pixels, palette, *palette_len))
|
||||
return -1;
|
||||
break;
|
||||
case MPNG_CHUNK_TRNS:
|
||||
if (format != FMT_ARGB8888 || !pixels || pixels != pixelsat)
|
||||
return -1;
|
||||
|
||||
if (ihdr->color_type == 2)
|
||||
{
|
||||
if (*palette_len == 0)
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
else if (ihdr->color_type == 3)
|
||||
return -1;
|
||||
else
|
||||
return -1;
|
||||
break;
|
||||
case MPNG_CHUNK_IDAT:
|
||||
{
|
||||
size_t byteshere;
|
||||
size_t chunklen_copy;
|
||||
#ifdef WANT_MINIZ
|
||||
tinfl_status status;
|
||||
#endif
|
||||
|
||||
if (!pixels || (ihdr->color_type == 3 && (*palette_len) == 0))
|
||||
return -1;
|
||||
|
||||
chunklen_copy = chunk.size;
|
||||
byteshere = (pixelsend - pixelsat)+1;
|
||||
|
||||
#ifdef WANT_MINIZ
|
||||
status = tinfl_decompress(inflator,
|
||||
(const mz_uint8 *)chunk.data,
|
||||
&chunklen_copy, pixels,
|
||||
pixelsat,
|
||||
&byteshere,
|
||||
TINFL_FLAG_HAS_MORE_INPUT |
|
||||
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF |
|
||||
TINFL_FLAG_PARSE_ZLIB_HEADER);
|
||||
#endif
|
||||
|
||||
pixelsat += byteshere;
|
||||
|
||||
#ifdef WANT_MINIZ
|
||||
if (status < TINFL_STATUS_DONE)
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case MPNG_CHUNK_IEND:
|
||||
{
|
||||
unsigned b, x, y;
|
||||
#ifdef WANT_MINIZ
|
||||
tinfl_status status;
|
||||
#endif
|
||||
size_t finalbytes;
|
||||
unsigned int bpp_packed;
|
||||
uint8_t *prevout;
|
||||
size_t zero = 0;
|
||||
uint8_t * out = NULL;
|
||||
uint8_t * filteredline = NULL;
|
||||
|
||||
if (data != data_end)
|
||||
return -1;
|
||||
if (chunk.size)
|
||||
return -1;
|
||||
memset(img, 0, sizeof(struct mpng_image));
|
||||
if (format!=FMT_RGB888 && format!=FMT_XRGB8888 && format!=FMT_ARGB8888) return false;
|
||||
|
||||
finalbytes = (pixelsend - pixelsat);
|
||||
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;
|
||||
|
||||
#ifdef WANT_MINIZ
|
||||
status = tinfl_decompress(inflator, (const mz_uint8 *)NULL, &zero, pixels, pixelsat, &finalbytes,
|
||||
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | TINFL_FLAG_PARSE_ZLIB_HEADER);
|
||||
#endif
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
uint8_t * pixels=NULL;
|
||||
uint8_t * pixelsat;
|
||||
uint8_t * pixelsend;
|
||||
|
||||
pixelsat += finalbytes;
|
||||
//chop off some warnings... these are all initialized in IHDR
|
||||
unsigned int bitsperchannel;
|
||||
unsigned int colortype;
|
||||
unsigned int compressiontype;
|
||||
unsigned int filtertype;
|
||||
unsigned int interlacetype;
|
||||
unsigned int bpl;
|
||||
|
||||
#ifdef WANT_MINIZ
|
||||
if (status < TINFL_STATUS_DONE)
|
||||
return -1;
|
||||
unsigned int palette[256];
|
||||
memset(palette, 0, sizeof(palette));//not gonna catch palette overflows
|
||||
int palettelen=0;
|
||||
|
||||
if (status > TINFL_STATUS_DONE)
|
||||
return -1;
|
||||
#endif
|
||||
/* Too little data (can't be too much
|
||||
* because we didn't give it that buffer size) */
|
||||
if (pixelsat != pixelsend)
|
||||
return -1;
|
||||
tinfl_decompressor inflator;
|
||||
tinfl_init(&inflator);
|
||||
|
||||
out = (uint8_t*)malloc(videofmt_byte_per_pixel(format) * ihdr->width * ihdr->height);
|
||||
|
||||
if (!out)
|
||||
return -1;
|
||||
|
||||
/* TODO: deinterlace at random point */
|
||||
|
||||
/* run filters */
|
||||
bpp_packed = ((ihdr->color_type == 2) ? 3 : (ihdr->color_type == 6) ? 4 : 1);
|
||||
prevout = (out + (4 * ihdr->width * 1));
|
||||
|
||||
/* This will blow up if a 1px high image
|
||||
* is filtered with Paeth, but highly unlikely. */
|
||||
if (ihdr->height==1)
|
||||
prevout = out;
|
||||
|
||||
/* Not using bpp here because we only need a chunk of black anyways */
|
||||
memset(prevout, 0, 4 * ihdr->width * 1);
|
||||
|
||||
filteredline = pixels;
|
||||
|
||||
for (y = 0; y < ihdr->height; y++)
|
||||
while (true)
|
||||
{
|
||||
uint8_t *thisout = (uint8_t*)(out + ((*bpl) * y));
|
||||
if (data+4+4>dataend) goto bad;
|
||||
unsigned int chunklen=read32r(data);
|
||||
unsigned int chunktype=read32r(data+4);
|
||||
if (chunklen>=0x80000000) goto bad;
|
||||
if (data+4+chunklen+4>dataend) goto bad;
|
||||
unsigned int chunkchecksum=mz_crc32(mz_crc32(0, NULL, 0), (uint8_t*)data+4, 4+chunklen);
|
||||
const uint8_t * chunkdata=data+4+4;
|
||||
unsigned int actualchunkchecksum=read32r(data+4+4+chunklen);
|
||||
if (actualchunkchecksum!=chunkchecksum) goto bad;
|
||||
|
||||
data+=4+4+chunklen+4;
|
||||
switch (chunktype)
|
||||
{
|
||||
case 0x49484452: //IHDR
|
||||
{
|
||||
read32(width);
|
||||
read32(height);
|
||||
read8(bitsperchannel);
|
||||
read8(colortype);
|
||||
read8(compressiontype);
|
||||
read8(filtertype);
|
||||
read8(interlacetype);
|
||||
|
||||
if (width>=0x80000000) goto bad;
|
||||
if (width==0) goto bad;
|
||||
if (height>=0x80000000) goto bad;
|
||||
if (height==0) goto bad;
|
||||
if (colortype!=2 && colortype!=3 && colortype!=6) goto bad;
|
||||
//Greyscale 0
|
||||
//Truecolour 2
|
||||
//Indexed-colour 3
|
||||
//Greyscale with alpha 4
|
||||
//Truecolour with alpha 6
|
||||
if (colortype==2 && bitsperchannel!=8) goto bad;//truecolor; can be 16bpp but I don't want that.
|
||||
if (colortype==3 && bitsperchannel!=1 && bitsperchannel!=2 && bitsperchannel!=4 && bitsperchannel!=8) goto bad;//paletted
|
||||
if (colortype==6 && bitsperchannel!=8) goto bad;//truecolor with alpha
|
||||
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;
|
||||
pixels=(uint8_t*)malloc((bpl+1)*height); if (!pixels) goto bad;
|
||||
pixelsat=pixels;
|
||||
pixelsend=pixels+(bpl+1)*height;
|
||||
}
|
||||
break;
|
||||
case 0x504c5445: //PLTE
|
||||
{
|
||||
if (pixels==NULL || palettelen!=0) goto bad;
|
||||
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;
|
||||
for (i=0;i<palettelen;i++)
|
||||
{
|
||||
read24(palette[i]);
|
||||
palette[i]|=0xFF000000;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x74524E53: //tRNS
|
||||
{
|
||||
if (format!=FMT_ARGB8888 || pixels==NULL || pixels!=pixelsat) goto bad;
|
||||
if (colortype==2)
|
||||
{
|
||||
if (palettelen==0) goto bad;
|
||||
goto bad;
|
||||
}
|
||||
else if (colortype==3)
|
||||
{
|
||||
goto bad;
|
||||
}
|
||||
else goto bad;
|
||||
}
|
||||
break;
|
||||
case 0x49444154: //IDAT
|
||||
{
|
||||
if (pixels==NULL || (colortype==3 && palettelen==0)) goto bad;
|
||||
size_t chunklencopy=chunklen;
|
||||
size_t byteshere=(pixelsend-pixelsat)+1;
|
||||
tinfl_status 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;
|
||||
if (status<0) goto bad;
|
||||
}
|
||||
break;
|
||||
case 0x49454e44: //IEND
|
||||
{
|
||||
if (data!=dataend) goto bad;
|
||||
if (chunklen) goto bad;
|
||||
size_t zero=0;
|
||||
size_t finalbytes=(pixelsend-pixelsat);
|
||||
tinfl_status status=tinfl_decompress(&inflator, (const uint8_t*)NULL, &zero, pixels, pixelsat, &finalbytes,
|
||||
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | TINFL_FLAG_PARSE_ZLIB_HEADER);
|
||||
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)
|
||||
uint8_t * out=(uint8_t*)malloc(videofmt_byte_per_pixel(format)*width*height);
|
||||
|
||||
//TODO: deinterlace at random point
|
||||
|
||||
//run filters
|
||||
unsigned int bpppacked=((colortype==2)?3:(colortype==6)?4:1);
|
||||
uint8_t * prevout=out+(4*width*1);
|
||||
if (height==1)
|
||||
{
|
||||
prevout=out;
|
||||
//this will blow up if a 1px high image is filtered with Paeth, but who the hell would do that?
|
||||
}
|
||||
memset(prevout, 0, 4*width*1);//not using bpp here because we only need a chunk of black anyways
|
||||
uint8_t * filteredline=pixels;
|
||||
|
||||
for (y=0;y<height;y++)
|
||||
{
|
||||
uint8_t * thisout=out+(bpl*y);
|
||||
switch (*(filteredline++))
|
||||
{
|
||||
case 0:
|
||||
memcpy(thisout, filteredline, (*bpl));
|
||||
memcpy(thisout, filteredline, bpl);
|
||||
break;
|
||||
case 1:
|
||||
memcpy(thisout, filteredline, bpp_packed);
|
||||
|
||||
for (x = bpp_packed; x < (*bpl); x++)
|
||||
thisout[x] = thisout[x - bpp_packed] + filteredline[x];
|
||||
memcpy(thisout, filteredline, bpppacked);
|
||||
for (x=bpppacked;x<bpl;x++)
|
||||
thisout[x]=thisout[x-bpppacked]+filteredline[x];
|
||||
break;
|
||||
case 2:
|
||||
for (x = 0; x < (*bpl); x++)
|
||||
thisout[x] = prevout[x] + filteredline[x];
|
||||
for (x=0;x<bpl;x++)
|
||||
{
|
||||
thisout[x]=prevout[x]+filteredline[x];
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
for (x = 0; x < bpp_packed; x++)
|
||||
for (x=0;x<bpppacked;x++)
|
||||
{
|
||||
int a = 0;
|
||||
int b = prevout[x];
|
||||
thisout[x] = (a+b)/2 + filteredline[x];
|
||||
int a=0;
|
||||
int b=prevout[x];
|
||||
thisout[x]=(a+b)/2+filteredline[x];
|
||||
}
|
||||
for (x = bpp_packed; x < (*bpl); x++)
|
||||
for (x=bpppacked;x<bpl;x++)
|
||||
{
|
||||
int a = thisout[x - bpp_packed];
|
||||
int b = prevout[x];
|
||||
thisout[x] = (a + b) / 2 + filteredline[x];
|
||||
int a=thisout[x-bpppacked];
|
||||
int b=prevout[x];
|
||||
thisout[x]=(a+b)/2+filteredline[x];
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
for (x = 0; x < bpp_packed; x++)
|
||||
for (x=0;x<bpppacked;x++)
|
||||
{
|
||||
int prediction;
|
||||
|
||||
int a = 0;
|
||||
int b = prevout[x];
|
||||
int c = 0;
|
||||
int a=0;
|
||||
int b=prevout[x];
|
||||
int c=0;
|
||||
|
||||
int p = a+b-c;
|
||||
int pa = abs(p-a);
|
||||
int pb = abs(p-b);
|
||||
int pc = abs(p-c);
|
||||
int p=a+b-c;
|
||||
int pa=abs(p-a);
|
||||
int pb=abs(p-b);
|
||||
int pc=abs(p-c);
|
||||
|
||||
if (pa <= pb && pa <= pc)
|
||||
prediction=a;
|
||||
else if (pb <= pc)
|
||||
prediction=b;
|
||||
else
|
||||
prediction=c;
|
||||
if (pa<=pb && pa<=pc) prediction=a;
|
||||
else if (pb<=pc) prediction=b;
|
||||
else prediction=c;
|
||||
|
||||
thisout[x] = filteredline[x]+prediction;
|
||||
thisout[x]=filteredline[x]+prediction;
|
||||
}
|
||||
|
||||
for (x = bpp_packed; x < (*bpl); x++)
|
||||
for (x=bpppacked;x<bpl;x++)
|
||||
{
|
||||
int prediction;
|
||||
|
||||
int a = thisout[x - bpp_packed];
|
||||
int b = prevout[x];
|
||||
int c = prevout[x - bpp_packed];
|
||||
int a=thisout[x-bpppacked];
|
||||
int b=prevout[x];
|
||||
int c=prevout[x-bpppacked];
|
||||
|
||||
int p = a+b-c;
|
||||
int pa = abs(p-a);
|
||||
int pb = abs(p-b);
|
||||
int pc = abs(p-c);
|
||||
int p=a+b-c;
|
||||
int pa=abs(p-a);
|
||||
int pb=abs(p-b);
|
||||
int pc=abs(p-c);
|
||||
|
||||
if (pa <= pb && pa <= pc)
|
||||
prediction = a;
|
||||
else if (pb <= pc)
|
||||
prediction = b;
|
||||
else
|
||||
prediction = c;
|
||||
if (pa<=pb && pa<=pc) prediction=a;
|
||||
else if (pb<=pc) prediction=b;
|
||||
else prediction=c;
|
||||
|
||||
thisout[x] = filteredline[x] + prediction;
|
||||
thisout[x]=filteredline[x]+prediction;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
default: goto bad;
|
||||
}
|
||||
prevout = thisout;
|
||||
filteredline += (*bpl);
|
||||
prevout=thisout;
|
||||
filteredline+=bpl;
|
||||
}
|
||||
|
||||
/* Unpack paletted data
|
||||
* not sure if these aliasing tricks are valid,
|
||||
* but the prerequisites for that bugging up
|
||||
* are pretty much impossible to hit.
|
||||
**/
|
||||
if (ihdr->color_type == 3)
|
||||
//unpack paletted data
|
||||
//not sure if these aliasing tricks are valid, but the prerequisites for that bugging up are pretty much impossible to hit.
|
||||
if (colortype==3)
|
||||
{
|
||||
switch (ihdr->depth)
|
||||
switch (bitsperchannel)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
int y = ihdr->height;
|
||||
uint8_t * outp = out + 3 * ihdr->width * ihdr->height;
|
||||
do
|
||||
{
|
||||
uint8_t * inp = (uint8_t*)(out + y * (*bpl));
|
||||
int x = (ihdr->width + 7) / 8;
|
||||
do
|
||||
{
|
||||
int y=height;
|
||||
uint8_t * outp=out+3*width*height;
|
||||
do {
|
||||
uint8_t * inp=out+y*bpl;
|
||||
|
||||
int x=(width+7)/8;
|
||||
do {
|
||||
x--;
|
||||
inp--;
|
||||
for (b = 0; b < 8; b++)
|
||||
for (b=0;b<8;b++)
|
||||
{
|
||||
int rgb32 = palette[((*inp)>>b)&1];
|
||||
*(--outp) = rgb32 >> 0;
|
||||
*(--outp) = rgb32 >> 8;
|
||||
*(--outp) = rgb32 >> 16;
|
||||
int rgb32=palette[((*inp)>>b)&1];
|
||||
*(--outp)=rgb32>>0;
|
||||
*(--outp)=rgb32>>8;
|
||||
*(--outp)=rgb32>>16;
|
||||
}
|
||||
} while(x);
|
||||
y--;
|
||||
@ -438,23 +324,21 @@ bool png_decode_iterate(const uint8_t *data, const uint8_t *data_end,
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
int y = ihdr->height;
|
||||
uint8_t * outp = (uint8_t*)(out + 3 * ihdr->width * ihdr->height);
|
||||
do
|
||||
{
|
||||
unsigned char *inp = out + y * (*bpl);
|
||||
int x = (ihdr->width + 3) / 4;
|
||||
do
|
||||
{
|
||||
int b;
|
||||
int y=height;
|
||||
uint8_t * outp=out+3*width*height;
|
||||
do {
|
||||
unsigned char * inp=out+y*bpl;
|
||||
|
||||
int x=(width+3)/4;
|
||||
do {
|
||||
x--;
|
||||
inp--;
|
||||
for (b = 0;b < 8; b += 2)
|
||||
for (b=0;b<8;b+=2)
|
||||
{
|
||||
int rgb32 = palette[((*inp)>>b)&3];
|
||||
*(--outp) = rgb32 >> 0;
|
||||
*(--outp) = rgb32 >> 8;
|
||||
*(--outp) = rgb32 >> 16;
|
||||
int rgb32=palette[((*inp)>>b)&3];
|
||||
*(--outp)=rgb32>>0;
|
||||
*(--outp)=rgb32>>8;
|
||||
*(--outp)=rgb32>>16;
|
||||
}
|
||||
} while(x);
|
||||
y--;
|
||||
@ -463,28 +347,23 @@ bool png_decode_iterate(const uint8_t *data, const uint8_t *data_end,
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
int y = ihdr->height;
|
||||
uint8_t *outp = out + 3 * ihdr->width * ihdr->height;
|
||||
|
||||
do
|
||||
{
|
||||
unsigned char *inp = out + y * (*bpl);
|
||||
int x = (ihdr->width + 1) / 2;
|
||||
|
||||
do
|
||||
{
|
||||
int rgb32;
|
||||
int y=height;
|
||||
uint8_t * outp=out+3*width*height;
|
||||
do {
|
||||
unsigned char * inp=out+y*bpl;
|
||||
|
||||
int x=(width+1)/2;
|
||||
do {
|
||||
x--;
|
||||
inp--;
|
||||
rgb32 = palette[*inp&15];
|
||||
*(--outp) = rgb32 >> 0;
|
||||
*(--outp) = rgb32 >> 8;
|
||||
*(--outp) = rgb32 >> 16;
|
||||
rgb32 = palette[*inp>>4];
|
||||
*(--outp) = rgb32 >> 0;
|
||||
*(--outp) = rgb32 >> 8;
|
||||
*(--outp) = rgb32 >> 16;
|
||||
int rgb32=palette[*inp&15];
|
||||
*(--outp)=rgb32>>0;
|
||||
*(--outp)=rgb32>>8;
|
||||
*(--outp)=rgb32>>16;
|
||||
rgb32=palette[*inp>>4];
|
||||
*(--outp)=rgb32>>0;
|
||||
*(--outp)=rgb32>>8;
|
||||
*(--outp)=rgb32>>16;
|
||||
} while(x);
|
||||
y--;
|
||||
} while(y);
|
||||
@ -492,121 +371,54 @@ bool png_decode_iterate(const uint8_t *data, const uint8_t *data_end,
|
||||
break;
|
||||
case 8:
|
||||
{
|
||||
uint8_t *inp = (uint8_t*)(out + ihdr->width * ihdr->height);
|
||||
uint8_t *outp = (uint8_t*)(out + 3 * ihdr->width * ihdr->height);
|
||||
int i = ihdr->width * ihdr->height;
|
||||
do
|
||||
{
|
||||
int rgb32;
|
||||
uint8_t * inp=out+width*height;
|
||||
uint8_t * outp=out+3*width*height;
|
||||
int i=width*height;
|
||||
do {
|
||||
i--;
|
||||
inp -= 1;
|
||||
rgb32 = palette[*inp];
|
||||
|
||||
*(--outp) = rgb32 >> 0;
|
||||
*(--outp) = rgb32 >> 8;
|
||||
*(--outp) = rgb32 >> 16;
|
||||
inp-=1;
|
||||
int rgb32=palette[*inp];
|
||||
*(--outp)=rgb32>>0;
|
||||
*(--outp)=rgb32>>8;
|
||||
*(--outp)=rgb32>>16;
|
||||
} while(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* unpack to 32bpp if requested */
|
||||
if (format != FMT_RGB888 && ihdr->color_type == 2)
|
||||
{
|
||||
uint8_t *inp = (uint8_t*)(out + ihdr->width * ihdr->height * 3);
|
||||
uint32_t *outp = (uint32_t*)(((uint32_t*)out) + ihdr->width * ihdr->height);
|
||||
int i = ihdr->width * ihdr->height;
|
||||
|
||||
do
|
||||
//unpack to 32bpp if requested
|
||||
if (format!=FMT_RGB888 && colortype==2)
|
||||
{
|
||||
uint8_t * inp=out+width*height*3;
|
||||
uint32_t * outp=((uint32_t*)out)+width*height;
|
||||
int i=width*height;
|
||||
do {
|
||||
i--;
|
||||
inp-=3;
|
||||
outp--;
|
||||
*outp = word_be(inp) | 0xFF000000;
|
||||
*outp = read24r(inp) | 0xFF000000;
|
||||
} while(i);
|
||||
}
|
||||
|
||||
img->width = ihdr->width;
|
||||
img->height = ihdr->height;
|
||||
img->pixels = out;
|
||||
img->pitch = videofmt_byte_per_pixel(format) * ihdr->width;
|
||||
img->format = format;
|
||||
img->width=width;
|
||||
img->height=height;
|
||||
img->pixels=out;
|
||||
img->pitch=videofmt_byte_per_pixel(format)*width;
|
||||
img->format=format;
|
||||
tinfl_deinit(&inflator);
|
||||
free(pixels);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!(chunk.type & 0x20000000))
|
||||
return -1; /* unknown critical */
|
||||
/* otherwise ignore */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool png_decode(const void *userdata, size_t len,
|
||||
struct mpng_image *img, enum video_format format)
|
||||
{
|
||||
struct mpng_ihdr ihdr = {0};
|
||||
unsigned int bpl;
|
||||
uint32_t palette[256];
|
||||
int palette_len = 0;
|
||||
uint8_t *pixelsat = NULL;
|
||||
uint8_t *pixelsend = NULL;
|
||||
uint8_t *pixels = NULL;
|
||||
const uint8_t *data_end = NULL;
|
||||
const uint8_t *data = (const uint8_t*)userdata;
|
||||
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
memset(img, 0, sizeof(struct mpng_image));
|
||||
|
||||
/* Only works for RGB888, XRGB8888, and ARGB8888 */
|
||||
if (format != FMT_RGB888
|
||||
&& format != FMT_XRGB8888
|
||||
&& format != FMT_ARGB8888)
|
||||
return false;
|
||||
|
||||
if (len < 8)
|
||||
return false;
|
||||
|
||||
if (memcmp(data, mpng_magic, sizeof(mpng_magic)) != 0)
|
||||
return false;
|
||||
|
||||
data_end = (const uint8_t*)(data + len);
|
||||
data += 8;
|
||||
|
||||
memset(palette, 0, sizeof(palette));
|
||||
|
||||
#ifdef WANT_MINIZ
|
||||
tinfl_decompressor inflator;
|
||||
tinfl_init(&inflator);
|
||||
#endif
|
||||
|
||||
while (1)
|
||||
{
|
||||
int ret = png_decode_iterate(data, data_end,
|
||||
&ihdr, img, palette, format, &bpl, &palette_len,
|
||||
pixels, pixelsat, pixelsend
|
||||
#ifdef WANT_MINIZ
|
||||
,&inflator
|
||||
#endif
|
||||
);
|
||||
|
||||
switch (ret)
|
||||
{
|
||||
case -1:
|
||||
goto error;
|
||||
case 1:
|
||||
return true;
|
||||
default:
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!(chunktype&0x20000000)) goto bad;//unknown critical
|
||||
//otherwise ignore
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
bad:
|
||||
tinfl_deinit(&inflator);
|
||||
free(pixels);
|
||||
memset(img, 0, sizeof(struct mpng_image));
|
||||
return false;
|
||||
|
Loading…
x
Reference in New Issue
Block a user