Restore original rpng.c

This commit is contained in:
twinaphex 2015-02-20 22:49:49 +01:00
parent 14fb2f6418
commit 2ac62e0cd3
2 changed files with 148 additions and 209 deletions

View File

@ -8,7 +8,7 @@ CFLAGS += -DHAVE_IMLIB2
LDFLAGS += -lImlib2
endif
SOURCES := rpng.c rpng_test.c ../../file/nbio/nbio_stdio.c
SOURCES := rpng_nbio.c rpng_test.c ../../file/nbio/nbio_stdio.c
OBJS := $(SOURCES:.c=.o)
CFLAGS += -Wall -pedantic -std=gnu99 -O0 -g -DHAVE_ZLIB -DHAVE_ZLIB_DEFLATE -DRPNG_TEST -I../../include

View File

@ -28,8 +28,6 @@
#include <stdlib.h>
#include <string.h>
#include <file/nbio.h>
#ifdef GEKKO
#include <malloc.h>
#endif
@ -127,12 +125,21 @@ static enum png_chunk_type png_chunk_type(const struct png_chunk *chunk)
return PNG_CHUNK_NOOP;
}
static bool png_alloc_chunk(struct png_chunk *chunk)
static bool png_read_chunk(FILE *file, struct png_chunk *chunk)
{
free(chunk->data);
chunk->data = (uint8_t*)calloc(1, chunk->size + sizeof(uint32_t)); /* CRC32 */
if (!chunk->data)
return false;
if (fread(chunk->data, 1, chunk->size +
sizeof(uint32_t), file) != (chunk->size + sizeof(uint32_t)))
{
free(chunk->data);
return false;
}
/* Ignore CRC. */
return true;
}
@ -145,12 +152,15 @@ static void png_free_chunk(struct png_chunk *chunk)
chunk->data = NULL;
}
static bool png_parse_ihdr(struct png_chunk *chunk,
struct png_ihdr *ihdr)
static bool png_parse_ihdr(FILE *file,
struct png_chunk *chunk, struct png_ihdr *ihdr)
{
unsigned i;
bool ret = true;
if (!png_read_chunk(file, chunk))
return false;
if (chunk->size != 13)
GOTO_END_ERROR();
@ -597,7 +607,8 @@ static bool png_reverse_filter_adam7(uint32_t *data,
return true;
}
static bool png_realloc_idat(const struct png_chunk *chunk, struct idat_buffer *buf)
static bool png_append_idat(FILE *file,
const struct png_chunk *chunk, struct idat_buffer *buf)
{
uint8_t *new_buffer = (uint8_t*)realloc(buf->data, buf->size + chunk->size);
@ -605,14 +616,25 @@ static bool png_realloc_idat(const struct png_chunk *chunk, struct idat_buffer *
return false;
buf->data = new_buffer;
if (fread(buf->data + buf->size, 1, chunk->size, file) != chunk->size)
return false;
if (fseek(file, sizeof(uint32_t), SEEK_CUR) < 0)
return false;
buf->size += chunk->size;
return true;
}
static bool png_read_plte_into_buf(uint32_t *buffer, unsigned entries)
static bool png_read_plte(FILE *file, uint32_t *buffer, unsigned entries)
{
unsigned i;
uint8_t buf[256 * 3];
if (entries > 256)
return false;
if (fread(buf, 3, entries, file) != entries)
return false;
for (i = 0; i < entries; i++)
{
uint32_t r = buf[3 * i + 0];
@ -621,172 +643,9 @@ static bool png_read_plte_into_buf(uint32_t *buffer, unsigned entries)
buffer[i] = (r << 16) | (g << 8) | (b << 0) | (0xffu << 24);
}
return true;
}
bool rpng_load_image_argb_iterate(FILE *file, struct png_chunk *chunk,
uint32_t *palette,
struct png_ihdr *ihdr, struct idat_buffer *idat_buf,
bool *has_ihdr, bool *has_idat,
bool *has_iend, bool *has_plte, size_t *increment_size)
{
switch (png_chunk_type(chunk))
{
case PNG_CHUNK_NOOP:
default:
*increment_size = chunk->size + sizeof(uint32_t);
break;
case PNG_CHUNK_ERROR:
return false;
case PNG_CHUNK_IHDR:
if (*has_ihdr || *has_idat || *has_iend)
return false;
if (!png_alloc_chunk(chunk))
return false;
if (fread(chunk->data, 1, chunk->size +
sizeof(uint32_t), file) != (chunk->size + sizeof(uint32_t)))
{
free(chunk->data);
return false;
}
if (!png_parse_ihdr(chunk, ihdr))
return false;
*has_ihdr = true;
break;
case PNG_CHUNK_PLTE:
{
unsigned entries = chunk->size / 3;
if (!*has_ihdr || *has_plte || *has_iend || *has_idat)
return false;
if (chunk->size % 3)
return false;
if (entries > 256)
return false;
if (fread(&palette, 3, entries, file) != entries)
return false;
if (!png_read_plte_into_buf(palette, entries))
return false;
*increment_size = sizeof(uint32_t);
*has_plte = true;
}
break;
case PNG_CHUNK_IDAT:
{
if (!(*has_ihdr) || *has_iend || (ihdr->color_type == 3 && !(*has_plte)))
return false;
if (!png_realloc_idat(chunk, idat_buf))
return false;
if (fread(idat_buf->data + idat_buf->size, 1, chunk->size, file) != chunk->size)
return false;
*increment_size = sizeof(uint32_t);
idat_buf->size += chunk->size;
*has_idat = true;
}
break;
case PNG_CHUNK_IEND:
if (!(*has_ihdr) || !(*has_idat))
return false;
*increment_size = sizeof(uint32_t);
*has_iend = true;
break;
}
return true;
}
bool rpng_load_image_argb_process(uint8_t *inflate_buf,
struct png_ihdr *ihdr,
struct idat_buffer *idat_buf, uint32_t **data,
uint32_t *palette,
unsigned *width, unsigned *height)
{
z_stream stream = {0};
size_t inflate_buf_size = 0;
if (inflateInit(&stream) != Z_OK)
if (fseek(file, sizeof(uint32_t), SEEK_CUR) < 0)
return false;
png_pass_geom(ihdr, ihdr->width,
ihdr->height, NULL, NULL, &inflate_buf_size);
if (ihdr->interlace == 1) /* To be sure. */
inflate_buf_size *= 2;
inflate_buf = (uint8_t*)malloc(inflate_buf_size);
if (!inflate_buf)
return false;
stream.next_in = idat_buf->data;
stream.avail_in = idat_buf->size;
stream.avail_out = inflate_buf_size;
stream.next_out = inflate_buf;
if (inflate(&stream, Z_FINISH) != Z_STREAM_END)
{
inflateEnd(&stream);
return false;
}
inflateEnd(&stream);
*width = ihdr->width;
*height = ihdr->height;
#ifdef GEKKO
/* we often use these in textures, make sure they're 32-byte aligned */
*data = (uint32_t*)memalign(32, ihdr->width * ihdr->height * sizeof(uint32_t));
#else
*data = (uint32_t*)malloc(ihdr->width * ihdr->height * sizeof(uint32_t));
#endif
if (!*data)
return false;
if (ihdr->interlace == 1)
{
if (!png_reverse_filter_adam7(*data,
ihdr, inflate_buf, stream.total_out, palette))
return false;
}
else if (!png_reverse_filter(*data,
ihdr, inflate_buf, stream.total_out, palette))
return false;
return true;
}
static bool rpng_load_image_argb_init(FILE *file,
uint32_t **data,
unsigned *width, unsigned *height,
long *file_len)
{
*data = NULL;
*width = 0;
*height = 0;
fseek(file, 0, SEEK_END);
*file_len = ftell(file);
rewind(file);
return true;
}
@ -794,7 +653,11 @@ bool rpng_load_image_argb(const char *path, uint32_t **data,
unsigned *width, unsigned *height)
{
long pos, file_len;
FILE *file;
char header[8];
uint8_t *inflate_buf = NULL;
size_t inflate_buf_size = 0;
z_stream stream = {0};
struct idat_buffer idat_buf = {0};
struct png_ihdr ihdr = {0};
uint32_t palette[256] = {0};
@ -803,68 +666,144 @@ bool rpng_load_image_argb(const char *path, uint32_t **data,
bool has_iend = false;
bool has_plte = false;
bool ret = true;
FILE *file;
*data = NULL;
*width = 0;
*height = 0;
file = fopen(path, "rb");
if (!file)
{
fprintf(stderr, "Tried to open file: %s\n", path);
GOTO_END_ERROR();
}
return false;
if (!rpng_load_image_argb_init(file, data, width, height, &file_len))
fseek(file, 0, SEEK_END);
file_len = ftell(file);
rewind(file);
if (fread(header, 1, sizeof(header), file) != sizeof(header))
GOTO_END_ERROR();
{
char header[8];
if (fread(header, 1, sizeof(header), file) != sizeof(header))
return false;
if (memcmp(header, png_magic, sizeof(png_magic)) != 0)
return false;
}
if (memcmp(header, png_magic, sizeof(png_magic)) != 0)
GOTO_END_ERROR();
/* feof() apparently isn't triggered after a seek (IEND). */
for (pos = 0; pos < file_len && pos >= 0; pos = ftell(file))
for (pos = ftell(file);
pos < file_len && pos >= 0; pos = ftell(file))
{
size_t increment = 0;
struct png_chunk chunk = {0};
if (!read_chunk_header(file, &chunk))
GOTO_END_ERROR();
if (!rpng_load_image_argb_iterate(
file, &chunk, palette, &ihdr, &idat_buf,
&has_ihdr, &has_idat, &has_iend, &has_plte,
&increment))
GOTO_END_ERROR();
if (increment != 0)
switch (png_chunk_type(&chunk))
{
if (fseek(file, increment, SEEK_CUR) < 0)
GOTO_END_ERROR();
}
case PNG_CHUNK_NOOP:
default:
if (fseek(file, chunk.size + sizeof(uint32_t), SEEK_CUR) < 0)
GOTO_END_ERROR();
break;
case PNG_CHUNK_ERROR:
GOTO_END_ERROR();
case PNG_CHUNK_IHDR:
if (has_ihdr || has_idat || has_iend)
GOTO_END_ERROR();
if (!png_parse_ihdr(file, &chunk, &ihdr))
GOTO_END_ERROR();
has_ihdr = true;
break;
case PNG_CHUNK_PLTE:
if (!has_ihdr || has_plte || has_iend || has_idat)
GOTO_END_ERROR();
if (chunk.size % 3)
GOTO_END_ERROR();
if (!png_read_plte(file, palette, chunk.size / 3))
GOTO_END_ERROR();
has_plte = true;
break;
case PNG_CHUNK_IDAT:
if (!has_ihdr || has_iend || (ihdr.color_type == 3 && !has_plte))
GOTO_END_ERROR();
if (!png_append_idat(file, &chunk, &idat_buf))
GOTO_END_ERROR();
has_idat = true;
break;
case PNG_CHUNK_IEND:
if (!has_ihdr || !has_idat)
GOTO_END_ERROR();
if (fseek(file, sizeof(uint32_t), SEEK_CUR) < 0)
GOTO_END_ERROR();
has_iend = true;
break;
}
}
if (!has_ihdr || !has_idat || !has_iend)
GOTO_END_ERROR();
rpng_load_image_argb_process(inflate_buf,
&ihdr, &idat_buf, data, palette,
width, height);
if (inflateInit(&stream) != Z_OK)
GOTO_END_ERROR();
png_pass_geom(&ihdr, ihdr.width, ihdr.height, NULL, NULL, &inflate_buf_size);
if (ihdr.interlace == 1) /* To be sure. */
inflate_buf_size *= 2;
inflate_buf = (uint8_t*)malloc(inflate_buf_size);
if (!inflate_buf)
GOTO_END_ERROR();
stream.next_in = idat_buf.data;
stream.avail_in = idat_buf.size;
stream.avail_out = inflate_buf_size;
stream.next_out = inflate_buf;
if (inflate(&stream, Z_FINISH) != Z_STREAM_END)
{
inflateEnd(&stream);
GOTO_END_ERROR();
}
inflateEnd(&stream);
*width = ihdr.width;
*height = ihdr.height;
#ifdef GEKKO
/* we often use these in textures, make sure they're 32-byte aligned */
*data = (uint32_t*)memalign(32, ihdr.width * ihdr.height * sizeof(uint32_t));
#else
*data = (uint32_t*)malloc(ihdr.width * ihdr.height * sizeof(uint32_t));
#endif
if (!*data)
GOTO_END_ERROR();
if (ihdr.interlace == 1)
{
if (!png_reverse_filter_adam7(*data,
&ihdr, inflate_buf, stream.total_out, palette))
GOTO_END_ERROR();
}
else if (!png_reverse_filter(*data,
&ihdr, inflate_buf, stream.total_out, palette))
GOTO_END_ERROR();
end:
if (file)
fclose(file);
if (!ret)
free(*data);
if (idat_buf.data)
free(idat_buf.data);
if (inflate_buf)
free(inflate_buf);
free(idat_buf.data);
free(inflate_buf);
return ret;
}