2015-02-17 03:35:14 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <string.h>
|
2015-02-17 03:45:32 +01:00
|
|
|
#include <boolean.h>
|
2015-02-17 03:35:14 +01:00
|
|
|
|
|
|
|
#ifdef WANT_MINIZ
|
|
|
|
#define MINIZ_HEADER_FILE_ONLY
|
|
|
|
#include "miniz.c"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <formats/mpng.h>
|
|
|
|
|
|
|
|
static uint32_t dword_be(const uint8_t *buf)
|
|
|
|
{
|
|
|
|
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3] << 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t word_be(const uint8_t *buf)
|
|
|
|
{
|
|
|
|
return (buf[0] << 16) | (buf[1] << 8) | (buf[2] << 0);
|
|
|
|
}
|
|
|
|
|
2015-02-17 04:30:27 +01:00
|
|
|
#define read24(target, chunkdata) do { target = word_be(chunkdata); chunkdata += 3; } while(0)
|
2015-02-17 03:35:14 +01:00
|
|
|
|
|
|
|
enum mpng_chunk_type
|
|
|
|
{
|
|
|
|
MPNG_CHUNK_TRNS = 0x74524E53,
|
|
|
|
MPNG_CHUNK_IHDR = 0x49484452,
|
|
|
|
MPNG_CHUNK_IDAT = 0x49444154,
|
|
|
|
MPNG_CHUNK_PLTE = 0x504c5445,
|
|
|
|
MPNG_CHUNK_IEND = 0x49454e44,
|
2015-02-17 03:45:32 +01:00
|
|
|
};
|
2015-02-17 03:35:14 +01:00
|
|
|
|
2015-02-17 04:30:27 +01:00
|
|
|
struct mpng_ihdr
|
|
|
|
{
|
|
|
|
uint32_t width;
|
|
|
|
uint32_t height;
|
|
|
|
uint8_t depth;
|
|
|
|
uint8_t color_type;
|
|
|
|
uint8_t compression;
|
|
|
|
uint8_t filter;
|
|
|
|
uint8_t interlace;
|
|
|
|
};
|
|
|
|
|
2015-02-17 04:59:39 +01:00
|
|
|
static const uint8_t mpng_magic[8] = {
|
|
|
|
0x89, 'P', 'N', 'G', 0x0d, 0x0a, 0x1a, 0x0a,
|
|
|
|
};
|
|
|
|
|
2015-02-17 04:36:19 +01:00
|
|
|
struct mpng_chunk
|
|
|
|
{
|
|
|
|
uint32_t size;
|
|
|
|
uint32_t type;
|
|
|
|
const uint8_t *data;
|
|
|
|
};
|
|
|
|
|
|
|
|
bool mpng_parse_ihdr(struct mpng_ihdr *ihdr, struct mpng_chunk *chunk,
|
2015-02-17 04:30:27 +01:00
|
|
|
enum video_format format, unsigned int *bpl,
|
|
|
|
uint8_t *pixels, uint8_t *pixelsat, uint8_t *pixelsend)
|
|
|
|
{
|
2015-02-17 04:59:39 +01:00
|
|
|
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];
|
2015-02-17 04:30:27 +01:00
|
|
|
|
2015-02-17 04:59:39 +01:00
|
|
|
if (ihdr->width == 0 || ihdr->height == 0)
|
2015-02-17 04:30:27 +01:00
|
|
|
return false;
|
|
|
|
|
2015-02-17 04:59:39 +01:00
|
|
|
if (ihdr->width >= 0x80000000)
|
2015-02-17 04:30:27 +01:00
|
|
|
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;
|
|
|
|
|
2015-02-17 04:59:39 +01:00
|
|
|
pixelsat = (uint8_t*)pixels;
|
|
|
|
pixelsend = (uint8_t*)(pixels + (*bpl + 1) * ihdr->height);
|
2015-02-17 04:30:27 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-02-17 05:12:09 +01:00
|
|
|
static bool mpng_read_plte(struct mpng_ihdr *ihdr,
|
|
|
|
struct mpng_chunk *chunk,
|
|
|
|
uint8_t *pixels,
|
|
|
|
uint32_t *buffer, unsigned entries)
|
|
|
|
{
|
|
|
|
unsigned i;
|
|
|
|
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++)
|
|
|
|
{
|
|
|
|
read24(buffer[i], chunk->data);
|
|
|
|
buffer[i] |= 0xFF000000;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2015-02-17 04:36:19 +01:00
|
|
|
|
2015-02-17 03:35:14 +01:00
|
|
|
bool png_decode(const void *userdata, size_t len,
|
2015-02-17 03:45:32 +01:00
|
|
|
struct mpng_image *img, enum video_format format)
|
2015-02-17 03:35:14 +01:00
|
|
|
{
|
2015-02-17 04:30:27 +01:00
|
|
|
struct mpng_ihdr ihdr = {0};
|
2015-02-17 03:35:14 +01:00
|
|
|
unsigned int bpl;
|
2015-02-17 05:12:09 +01:00
|
|
|
uint32_t palette[256];
|
2015-02-17 03:35:14 +01:00
|
|
|
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;
|
|
|
|
|
2015-02-17 03:45:32 +01:00
|
|
|
memset(img, 0, sizeof(struct mpng_image));
|
2015-02-17 03:35:14 +01:00
|
|
|
|
|
|
|
/* Only works for RGB888, XRGB8888, and ARGB8888 */
|
2015-02-17 03:45:32 +01:00
|
|
|
if (format != FMT_RGB888
|
|
|
|
&& format != FMT_XRGB8888
|
|
|
|
&& format != FMT_ARGB8888)
|
2015-02-17 03:35:14 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
if (len < 8)
|
|
|
|
return false;
|
|
|
|
|
2015-02-17 04:59:39 +01:00
|
|
|
if (memcmp(data, mpng_magic, sizeof(mpng_magic)) != 0)
|
2015-02-17 03:35:14 +01:00
|
|
|
return false;
|
|
|
|
|
2015-02-17 04:59:39 +01:00
|
|
|
data_end = (const uint8_t*)(data + len);
|
2015-02-17 03:35:14 +01:00
|
|
|
data += 8;
|
|
|
|
|
|
|
|
memset(palette, 0, sizeof(palette));
|
|
|
|
|
|
|
|
#ifdef WANT_MINIZ
|
|
|
|
tinfl_decompressor inflator;
|
|
|
|
tinfl_init(&inflator);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
unsigned int chunkchecksum;
|
|
|
|
unsigned int actualchunkchecksum;
|
2015-02-17 04:36:19 +01:00
|
|
|
struct mpng_chunk chunk = {0};
|
2015-02-17 03:35:14 +01:00
|
|
|
|
|
|
|
if ((data + 4 + 4) > data_end)
|
|
|
|
goto error;
|
|
|
|
|
2015-02-17 04:36:19 +01:00
|
|
|
chunk.size = dword_be(data);
|
2015-02-17 04:59:39 +01:00
|
|
|
chunk.type = dword_be(data + 4);
|
2015-02-17 03:35:14 +01:00
|
|
|
|
2015-02-17 04:36:19 +01:00
|
|
|
if (chunk.size >= 0x80000000)
|
2015-02-17 03:35:14 +01:00
|
|
|
goto error;
|
2015-02-17 04:36:19 +01:00
|
|
|
if ((data + 4 + chunk.size + 4) > data_end)
|
2015-02-17 03:35:14 +01:00
|
|
|
goto error;
|
|
|
|
|
2015-02-17 04:36:19 +01:00
|
|
|
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);
|
2015-02-17 03:35:14 +01:00
|
|
|
|
|
|
|
if (actualchunkchecksum != chunkchecksum)
|
|
|
|
goto error;
|
|
|
|
|
2015-02-17 04:36:19 +01:00
|
|
|
data += 4 + 4 + chunk.size + 4;
|
2015-02-17 03:35:14 +01:00
|
|
|
|
2015-02-17 04:36:19 +01:00
|
|
|
switch (chunk.type)
|
2015-02-17 03:35:14 +01:00
|
|
|
{
|
|
|
|
case MPNG_CHUNK_IHDR:
|
2015-02-17 04:36:19 +01:00
|
|
|
if (!mpng_parse_ihdr(&ihdr, &chunk, format, &bpl, pixels,
|
2015-02-17 04:30:27 +01:00
|
|
|
pixelsat, pixelsend))
|
|
|
|
goto error;
|
2015-02-17 03:35:14 +01:00
|
|
|
break;
|
|
|
|
case MPNG_CHUNK_PLTE:
|
2015-02-17 05:17:24 +01:00
|
|
|
if (chunk.size % 3)
|
|
|
|
goto error;
|
2015-02-17 03:35:14 +01:00
|
|
|
|
2015-02-17 05:17:24 +01:00
|
|
|
palette_len = chunk.size / 3;
|
2015-02-17 03:35:14 +01:00
|
|
|
|
2015-02-17 05:17:24 +01:00
|
|
|
if (!mpng_read_plte(&ihdr, &chunk, pixels, palette, palette_len))
|
|
|
|
goto error;
|
2015-02-17 03:35:14 +01:00
|
|
|
break;
|
|
|
|
case MPNG_CHUNK_TRNS:
|
2015-02-17 05:17:24 +01:00
|
|
|
if (format != FMT_ARGB8888 || !pixels || pixels != pixelsat)
|
|
|
|
goto error;
|
2015-02-17 03:35:14 +01:00
|
|
|
|
2015-02-17 05:17:24 +01:00
|
|
|
if (ihdr.color_type == 2)
|
|
|
|
{
|
|
|
|
if (palette_len == 0)
|
2015-02-17 03:35:14 +01:00
|
|
|
goto error;
|
2015-02-17 05:17:24 +01:00
|
|
|
goto error;
|
2015-02-17 03:35:14 +01:00
|
|
|
}
|
2015-02-17 05:17:24 +01:00
|
|
|
else if (ihdr.color_type == 3)
|
|
|
|
goto error;
|
|
|
|
else
|
|
|
|
goto error;
|
2015-02-17 03:35:14 +01:00
|
|
|
break;
|
|
|
|
case MPNG_CHUNK_IDAT:
|
|
|
|
{
|
|
|
|
size_t byteshere;
|
|
|
|
size_t chunklen_copy;
|
2015-02-17 03:45:32 +01:00
|
|
|
#ifdef WANT_MINIZ
|
|
|
|
tinfl_status status;
|
|
|
|
#endif
|
2015-02-17 03:35:14 +01:00
|
|
|
|
2015-02-17 04:30:27 +01:00
|
|
|
if (!pixels || (ihdr.color_type == 3 && palette_len == 0))
|
2015-02-17 03:35:14 +01:00
|
|
|
goto error;
|
|
|
|
|
2015-02-17 04:36:19 +01:00
|
|
|
chunklen_copy = chunk.size;
|
2015-02-17 03:35:14 +01:00
|
|
|
byteshere = (pixelsend - pixelsat)+1;
|
|
|
|
|
|
|
|
#ifdef WANT_MINIZ
|
2015-02-17 03:45:32 +01:00
|
|
|
status = tinfl_decompress(&inflator,
|
2015-02-17 04:36:19 +01:00
|
|
|
(const mz_uint8 *)chunk.data,
|
2015-02-17 03:35:14 +01:00
|
|
|
&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;
|
|
|
|
|
2015-02-17 03:45:32 +01:00
|
|
|
#ifdef WANT_MINIZ
|
2015-02-17 03:35:14 +01:00
|
|
|
if (status < TINFL_STATUS_DONE)
|
|
|
|
goto error;
|
2015-02-17 03:45:32 +01:00
|
|
|
#endif
|
2015-02-17 03:35:14 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MPNG_CHUNK_IEND:
|
|
|
|
{
|
2015-02-17 03:45:32 +01:00
|
|
|
unsigned b, x, y;
|
2015-02-17 03:35:14 +01:00
|
|
|
#ifdef WANT_MINIZ
|
|
|
|
tinfl_status status;
|
|
|
|
#endif
|
|
|
|
size_t finalbytes;
|
2015-02-17 03:49:39 +01:00
|
|
|
unsigned int bpp_packed;
|
|
|
|
uint8_t *prevout;
|
2015-02-17 03:35:14 +01:00
|
|
|
size_t zero = 0;
|
|
|
|
uint8_t * out = NULL;
|
|
|
|
uint8_t * filteredline = NULL;
|
|
|
|
|
|
|
|
if (data != data_end)
|
|
|
|
goto error;
|
2015-02-17 04:36:19 +01:00
|
|
|
if (chunk.size)
|
2015-02-17 03:35:14 +01:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
finalbytes = (pixelsend - pixelsat);
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
|
|
|
pixelsat += finalbytes;
|
|
|
|
|
2015-02-17 03:45:32 +01:00
|
|
|
#ifdef WANT_MINIZ
|
2015-02-17 03:35:14 +01:00
|
|
|
if (status < TINFL_STATUS_DONE)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (status > TINFL_STATUS_DONE)
|
|
|
|
goto error;
|
2015-02-17 03:45:32 +01:00
|
|
|
#endif
|
2015-02-17 03:49:39 +01:00
|
|
|
/* Too little data (can't be too much
|
|
|
|
* because we didn't give it that buffer size) */
|
2015-02-17 03:35:14 +01:00
|
|
|
if (pixelsat != pixelsend)
|
2015-02-17 03:49:39 +01:00
|
|
|
goto error;
|
2015-02-17 03:35:14 +01:00
|
|
|
|
2015-02-17 04:30:27 +01:00
|
|
|
out = (uint8_t*)malloc(videofmt_byte_per_pixel(format) * ihdr.width * ihdr.height);
|
2015-02-17 03:35:14 +01:00
|
|
|
|
2015-02-17 05:17:24 +01:00
|
|
|
if (!out)
|
|
|
|
goto error;
|
|
|
|
|
2015-02-17 03:49:39 +01:00
|
|
|
/* TODO: deinterlace at random point */
|
2015-02-17 03:35:14 +01:00
|
|
|
|
2015-02-17 03:49:39 +01:00
|
|
|
/* run filters */
|
2015-02-17 04:30:27 +01:00
|
|
|
bpp_packed = ((ihdr.color_type == 2) ? 3 : (ihdr.color_type == 6) ? 4 : 1);
|
|
|
|
prevout = (out + (4 * ihdr.width * 1));
|
2015-02-17 03:45:32 +01:00
|
|
|
|
2015-02-17 03:49:39 +01:00
|
|
|
/* This will blow up if a 1px high image
|
|
|
|
* is filtered with Paeth, but highly unlikely. */
|
2015-02-17 04:30:27 +01:00
|
|
|
if (ihdr.height==1)
|
|
|
|
prevout = out;
|
2015-02-17 03:35:14 +01:00
|
|
|
|
|
|
|
/* Not using bpp here because we only need a chunk of black anyways */
|
2015-02-17 04:30:27 +01:00
|
|
|
memset(prevout, 0, 4 * ihdr.width * 1);
|
2015-02-17 03:35:14 +01:00
|
|
|
|
|
|
|
filteredline = pixels;
|
|
|
|
|
2015-02-17 04:30:27 +01:00
|
|
|
for (y = 0; y < ihdr.height; y++)
|
2015-02-17 03:35:14 +01:00
|
|
|
{
|
2015-02-17 03:49:39 +01:00
|
|
|
uint8_t *thisout = (uint8_t*)(out + (bpl*y));
|
2015-02-17 03:35:14 +01:00
|
|
|
|
|
|
|
switch (*(filteredline++))
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
memcpy(thisout, filteredline, bpl);
|
|
|
|
break;
|
|
|
|
case 1:
|
2015-02-17 03:49:39 +01:00
|
|
|
memcpy(thisout, filteredline, bpp_packed);
|
2015-02-17 03:35:14 +01:00
|
|
|
|
2015-02-17 03:49:39 +01:00
|
|
|
for (x = bpp_packed; x < bpl; x++)
|
|
|
|
thisout[x] = thisout[x - bpp_packed] + filteredline[x];
|
2015-02-17 03:35:14 +01:00
|
|
|
break;
|
|
|
|
case 2:
|
2015-02-17 03:45:32 +01:00
|
|
|
for (x = 0; x < bpl; x++)
|
|
|
|
thisout[x] = prevout[x] + filteredline[x];
|
2015-02-17 03:35:14 +01:00
|
|
|
break;
|
|
|
|
case 3:
|
2015-02-17 03:49:39 +01:00
|
|
|
for (x = 0; x < bpp_packed; x++)
|
2015-02-17 03:35:14 +01:00
|
|
|
{
|
2015-02-17 03:45:32 +01:00
|
|
|
int a = 0;
|
|
|
|
int b = prevout[x];
|
|
|
|
thisout[x] = (a+b)/2 + filteredline[x];
|
2015-02-17 03:35:14 +01:00
|
|
|
}
|
2015-02-17 03:49:39 +01:00
|
|
|
for (x = bpp_packed; x < bpl; x++)
|
2015-02-17 03:35:14 +01:00
|
|
|
{
|
2015-02-17 03:49:39 +01:00
|
|
|
int a = thisout[x - bpp_packed];
|
2015-02-17 03:35:14 +01:00
|
|
|
int b = prevout[x];
|
|
|
|
thisout[x] = (a + b) / 2 + filteredline[x];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 4:
|
2015-02-17 03:49:39 +01:00
|
|
|
for (x = 0; x < bpp_packed; x++)
|
2015-02-17 03:35:14 +01:00
|
|
|
{
|
|
|
|
int prediction;
|
|
|
|
|
2015-02-17 03:45:32 +01:00
|
|
|
int a = 0;
|
|
|
|
int b = prevout[x];
|
|
|
|
int c = 0;
|
2015-02-17 03:35:14 +01:00
|
|
|
|
2015-02-17 03:45:32 +01:00
|
|
|
int p = a+b-c;
|
|
|
|
int pa = abs(p-a);
|
|
|
|
int pb = abs(p-b);
|
|
|
|
int pc = abs(p-c);
|
2015-02-17 03:35:14 +01:00
|
|
|
|
2015-02-17 03:45:32 +01:00
|
|
|
if (pa <= pb && pa <= pc)
|
|
|
|
prediction=a;
|
|
|
|
else if (pb <= pc)
|
|
|
|
prediction=b;
|
|
|
|
else
|
|
|
|
prediction=c;
|
2015-02-17 03:35:14 +01:00
|
|
|
|
2015-02-17 03:45:32 +01:00
|
|
|
thisout[x] = filteredline[x]+prediction;
|
2015-02-17 03:35:14 +01:00
|
|
|
}
|
2015-02-17 03:45:32 +01:00
|
|
|
|
2015-02-17 03:49:39 +01:00
|
|
|
for (x = bpp_packed; x < bpl; x++)
|
2015-02-17 03:35:14 +01:00
|
|
|
{
|
|
|
|
int prediction;
|
|
|
|
|
2015-02-17 03:49:39 +01:00
|
|
|
int a = thisout[x - bpp_packed];
|
2015-02-17 03:45:32 +01:00
|
|
|
int b = prevout[x];
|
2015-02-17 03:49:39 +01:00
|
|
|
int c = prevout[x - bpp_packed];
|
2015-02-17 03:35:14 +01:00
|
|
|
|
2015-02-17 03:45:32 +01:00
|
|
|
int p = a+b-c;
|
|
|
|
int pa = abs(p-a);
|
|
|
|
int pb = abs(p-b);
|
|
|
|
int pc = abs(p-c);
|
2015-02-17 03:35:14 +01:00
|
|
|
|
2015-02-17 03:45:32 +01:00
|
|
|
if (pa <= pb && pa <= pc)
|
|
|
|
prediction = a;
|
|
|
|
else if (pb <= pc)
|
|
|
|
prediction = b;
|
|
|
|
else
|
|
|
|
prediction = c;
|
2015-02-17 03:35:14 +01:00
|
|
|
|
2015-02-17 03:45:32 +01:00
|
|
|
thisout[x] = filteredline[x] + prediction;
|
2015-02-17 03:35:14 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
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.
|
|
|
|
**/
|
2015-02-17 04:30:27 +01:00
|
|
|
if (ihdr.color_type == 3)
|
2015-02-17 03:35:14 +01:00
|
|
|
{
|
2015-02-17 04:30:27 +01:00
|
|
|
switch (ihdr.depth)
|
2015-02-17 03:35:14 +01:00
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
{
|
2015-02-17 04:30:27 +01:00
|
|
|
int y = ihdr.height;
|
|
|
|
uint8_t * outp = out + 3 * ihdr.width * ihdr.height;
|
2015-02-17 03:35:14 +01:00
|
|
|
do
|
|
|
|
{
|
2015-02-17 04:30:27 +01:00
|
|
|
uint8_t * inp = (uint8_t*)(out + y * bpl);
|
|
|
|
int x = (ihdr.width + 7) / 8;
|
2015-02-17 03:35:14 +01:00
|
|
|
do
|
|
|
|
{
|
|
|
|
x--;
|
|
|
|
inp--;
|
2015-02-17 03:45:32 +01:00
|
|
|
for (b = 0; b < 8; b++)
|
2015-02-17 03:35:14 +01:00
|
|
|
{
|
2015-02-17 03:45:32 +01:00
|
|
|
int rgb32 = palette[((*inp)>>b)&1];
|
|
|
|
*(--outp) = rgb32 >> 0;
|
|
|
|
*(--outp) = rgb32 >> 8;
|
|
|
|
*(--outp) = rgb32 >> 16;
|
2015-02-17 03:35:14 +01:00
|
|
|
}
|
|
|
|
} while(x);
|
|
|
|
y--;
|
|
|
|
} while(y);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
{
|
2015-02-17 04:30:27 +01:00
|
|
|
int y = ihdr.height;
|
|
|
|
uint8_t * outp = (uint8_t*)(out + 3 * ihdr.width * ihdr.height);
|
2015-02-17 03:35:14 +01:00
|
|
|
do
|
|
|
|
{
|
|
|
|
unsigned char *inp = out + y * bpl;
|
2015-02-17 04:30:27 +01:00
|
|
|
int x = (ihdr.width + 3) / 4;
|
2015-02-17 03:35:14 +01:00
|
|
|
do
|
|
|
|
{
|
|
|
|
int b;
|
|
|
|
x--;
|
|
|
|
inp--;
|
|
|
|
for (b = 0;b < 8; b += 2)
|
|
|
|
{
|
2015-02-17 03:45:32 +01:00
|
|
|
int rgb32 = palette[((*inp)>>b)&3];
|
|
|
|
*(--outp) = rgb32 >> 0;
|
|
|
|
*(--outp) = rgb32 >> 8;
|
|
|
|
*(--outp) = rgb32 >> 16;
|
2015-02-17 03:35:14 +01:00
|
|
|
}
|
|
|
|
} while(x);
|
|
|
|
y--;
|
|
|
|
} while(y);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
{
|
2015-02-17 04:30:27 +01:00
|
|
|
int y = ihdr.height;
|
|
|
|
uint8_t *outp = out + 3 * ihdr.width * ihdr.height;
|
2015-02-17 03:35:14 +01:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
unsigned char *inp = out + y * bpl;
|
2015-02-17 04:30:27 +01:00
|
|
|
int x = (ihdr.width + 1) / 2;
|
2015-02-17 03:35:14 +01:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
int rgb32;
|
|
|
|
|
|
|
|
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;
|
|
|
|
} while(x);
|
|
|
|
y--;
|
|
|
|
} while(y);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
{
|
2015-02-17 04:30:27 +01:00
|
|
|
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;
|
2015-02-17 03:35:14 +01:00
|
|
|
do
|
|
|
|
{
|
|
|
|
int rgb32;
|
|
|
|
i--;
|
2015-02-17 03:45:32 +01:00
|
|
|
inp -= 1;
|
|
|
|
rgb32 = palette[*inp];
|
2015-02-17 03:35:14 +01:00
|
|
|
|
|
|
|
*(--outp) = rgb32 >> 0;
|
|
|
|
*(--outp) = rgb32 >> 8;
|
|
|
|
*(--outp) = rgb32 >> 16;
|
|
|
|
} while(i);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* unpack to 32bpp if requested */
|
2015-02-17 04:30:27 +01:00
|
|
|
if (format != FMT_RGB888 && ihdr.color_type == 2)
|
2015-02-17 03:35:14 +01:00
|
|
|
{
|
2015-02-17 04:30:27 +01:00
|
|
|
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;
|
2015-02-17 03:35:14 +01:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
i--;
|
|
|
|
inp-=3;
|
|
|
|
outp--;
|
|
|
|
*outp = word_be(inp) | 0xFF000000;
|
|
|
|
} while(i);
|
|
|
|
}
|
|
|
|
|
2015-02-17 04:30:27 +01:00
|
|
|
img->width = ihdr.width;
|
|
|
|
img->height = ihdr.height;
|
2015-02-17 03:35:14 +01:00
|
|
|
img->pixels = out;
|
2015-02-17 04:30:27 +01:00
|
|
|
img->pitch = videofmt_byte_per_pixel(format) * ihdr.width;
|
2015-02-17 03:35:14 +01:00
|
|
|
img->format = format;
|
|
|
|
free(pixels);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2015-02-17 04:36:19 +01:00
|
|
|
if (!(chunk.type & 0x20000000))
|
2015-02-17 04:59:39 +01:00
|
|
|
goto error; /* unknown critical */
|
|
|
|
/* otherwise ignore */
|
2015-02-17 03:35:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
error:
|
|
|
|
free(pixels);
|
2015-02-17 03:45:32 +01:00
|
|
|
memset(img, 0, sizeof(struct mpng_image));
|
2015-02-17 03:35:14 +01:00
|
|
|
return false;
|
|
|
|
}
|