diff --git a/include/cammy/sram.h b/include/cammy/sram.h index a757ffc..6c6c295 100644 --- a/include/cammy/sram.h +++ b/include/cammy/sram.h @@ -7,9 +7,18 @@ #define CAMMY_SRAM_SCRATCH_2_SIZE 4 #define CAMMY_SRAM_TILE_SIZE 16 +#define CAMMY_SRAM_TILE_WIDTH 8 +#define CAMMY_SRAM_TILE_HEIGHT 8 + #define CAMMY_SRAM_PHOTO_TILES_WIDTH 16 #define CAMMY_SRAM_PHOTO_TILES_HEIGHT 14 +#define CAMMY_SRAM_PHOTO_WIDTH \ + (CAMMY_SRAM_TILE_WIDTH * CAMMY_SRAM_PHOTO_TILES_WIDTH) + +#define CAMMY_SRAM_PHOTO_HEIGHT \ + (CAMMY_SRAM_TILE_HEIGHT * CAMMY_SRAM_PHOTO_TILES_HEIGHT) + #define CAMMY_SRAM_THUMB_TILES_WIDTH 4 #define CAMMY_SRAM_THUMB_TILES_HEIGHT 4 @@ -25,13 +34,13 @@ typedef struct _cammy_sram_tile { } cammy_sram_tile; typedef struct _cammy_sram_thumb { - cammy_sram_tile rows[CAMMY_SRAM_THUMB_TILES_HEIGHT] - [CAMMY_SRAM_THUMB_TILES_WIDTH]; + cammy_sram_tile tiles[CAMMY_SRAM_THUMB_TILES_HEIGHT] + [CAMMY_SRAM_THUMB_TILES_WIDTH]; } cammy_sram_thumb; typedef struct _cammy_sram_photo { - cammy_sram_tile rows[CAMMY_SRAM_PHOTO_TILES_HEIGHT] - [CAMMY_SRAM_PHOTO_TILES_WIDTH]; + cammy_sram_tile tiles[CAMMY_SRAM_PHOTO_TILES_HEIGHT] + [CAMMY_SRAM_PHOTO_TILES_WIDTH]; } cammy_sram_photo; typedef struct _cammy_sram_frame { diff --git a/src/Makefile b/src/Makefile index 1bb0e59..6f6994f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -5,10 +5,14 @@ HEADER_SUBDIR = cammy CC = $(CROSS)cc CFLAGS = $(CGFLAGS) -fPIC -Wall -O2 -I$(INCLUDE_PATH) -LDFLAGS = +LDFLAGS = -lz HEADERS = sram.h -OBJS = sram.o test.o +HEADERS_LOCAL = pnglite.h +HEADERS_BUILD = $(HEADERS_LOCAL) \ + $(addprefix $(INCLUDE_PATH)/$(HEADER_SUBDIR)/,$(HEADERS)) + +OBJS = sram.o pnglite.o test.o PROGRAM = test diff --git a/src/pnglite.c b/src/pnglite.c new file mode 100644 index 0000000..4899f24 --- /dev/null +++ b/src/pnglite.c @@ -0,0 +1,862 @@ +/* pnglite.c - pnglite library + For conditions of distribution and use, see copyright notice in pnglite.h +*/ +#define DO_CRC_CHECKS 1 +#define USE_ZLIB 1 + +#if USE_ZLIB +#include +#else +#include "zlite.h" +#endif + +#include +#include +#include +#include "pnglite.h" + +static png_alloc_t png_alloc; +static png_free_t png_free; + +static size_t file_read(png_t* png, void* out, size_t size, size_t numel) +{ + size_t result; + if(png->read_fun) + { + result = png->read_fun(out, size, numel, png->user_pointer); + } + else + { + if(!out) + { + result = fseek(png->user_pointer, (long)(size*numel), SEEK_CUR); + } + else + { + result = fread(out, size, numel, png->user_pointer); + } + } + + return result; +} + +static size_t file_write(png_t* png, void* p, size_t size, size_t numel) +{ + size_t result; + + if(png->write_fun) + { + result = png->write_fun(p, size, numel, png->user_pointer); + } + else + { + result = fwrite(p, size, numel, png->user_pointer); + } + + return result; +} + +static int file_read_ul(png_t* png, unsigned *out) +{ + unsigned char buf[4]; + + if(file_read(png, buf, 1, 4) != 4) + return PNG_FILE_ERROR; + + *out = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3]; + + return PNG_NO_ERROR; +} + +static int file_write_ul(png_t* png, unsigned in) +{ + unsigned char buf[4]; + + buf[0] = (in>>24) & 0xff; + buf[1] = (in>>16) & 0xff; + buf[2] = (in>>8) & 0xff; + buf[3] = (in) & 0xff; + + if(file_write(png, buf, 1, 4) != 4) + return PNG_FILE_ERROR; + + return PNG_NO_ERROR; +} + +static unsigned get_ul(unsigned char* buf) +{ + unsigned result; + unsigned char foo[4]; + + memcpy(foo, buf, 4); + + result = (foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | foo[3]; + + return result; +} + +static unsigned set_ul(unsigned char* buf, unsigned in) +{ + buf[0] = (in>>24) & 0xff; + buf[1] = (in>>16) & 0xff; + buf[2] = (in>>8) & 0xff; + buf[3] = (in) & 0xff; + + return PNG_NO_ERROR; +} + +int png_init(png_alloc_t pngalloc, png_free_t pngfree) +{ + if(pngalloc) + png_alloc = pngalloc; + else + png_alloc = &malloc; + + if(pngfree) + png_free = pngfree; + else + png_free = &free; + + return PNG_NO_ERROR; +} + +static int png_get_bpp(png_t* png) +{ + int bpp; + + switch(png->color_type) + { + case PNG_GREYSCALE: + bpp = 1; break; + case PNG_TRUECOLOR: + bpp = 3; break; + case PNG_INDEXED: + bpp = 1; break; + case PNG_GREYSCALE_ALPHA: + bpp = 2; break; + case PNG_TRUECOLOR_ALPHA: + bpp = 4; break; + default: + return PNG_FILE_ERROR; + } + + bpp *= png->depth/8; + + return bpp; +} + +static int png_read_ihdr(png_t* png) +{ + unsigned length; +#if DO_CRC_CHECKS + unsigned orig_crc; + unsigned calc_crc; +#endif + unsigned char ihdr[13+4]; /* length should be 13, make room for type (IHDR) */ + + file_read_ul(png, &length); + + if(length != 13) + { + printf("%d\n", length); + return PNG_CRC_ERROR; + } + + if(file_read(png, ihdr, 1, 13+4) != 13+4) + return PNG_EOF_ERROR; +#if DO_CRC_CHECKS + file_read_ul(png, &orig_crc); + + calc_crc = crc32(0L, 0, 0); + calc_crc = crc32(calc_crc, ihdr, 13+4); + + if(orig_crc != calc_crc) + return PNG_CRC_ERROR; +#else + file_read_ul(png); +#endif + + png->width = get_ul(ihdr+4); + png->height = get_ul(ihdr+8); + png->depth = ihdr[12]; + png->color_type = ihdr[13]; + png->compression_method = ihdr[14]; + png->filter_method = ihdr[15]; + png->interlace_method = ihdr[16]; + + if(png->color_type == PNG_INDEXED) + return PNG_NOT_SUPPORTED; + + if(png->depth != 8 && png->depth != 16) + return PNG_NOT_SUPPORTED; + + if(png->interlace_method) + return PNG_NOT_SUPPORTED; + + return PNG_NO_ERROR; +} + +static int png_write_ihdr(png_t* png) +{ + unsigned char ihdr[13+4]; + unsigned char *p = ihdr; + unsigned crc; + + file_write(png, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 1, 8); + + file_write_ul(png, 13); + + *p = 'I'; p++; + *p = 'H'; p++; + *p = 'D'; p++; + *p = 'R'; p++; + set_ul(p, png->width); p+=4; + set_ul(p, png->height); p+=4; + *p = png->depth; p++; + *p = png->color_type; p++; + *p = 0; p++; + *p = 0; p++; + *p = 0; p++; + + file_write(png, ihdr, 1, 13+4); + + crc = crc32(0L, 0, 0); + crc = crc32(crc, ihdr, 13+4); + + file_write_ul(png, crc); + + return PNG_NO_ERROR; +} + +void png_print_info(png_t* png) +{ + printf("PNG INFO:\n"); + printf("\twidth:\t\t%d\n", png->width); + printf("\theight:\t\t%d\n", png->height); + printf("\tdepth:\t\t%d\n", png->depth); + printf("\tcolor:\t\t"); + + switch(png->color_type) + { + case PNG_GREYSCALE: printf("greyscale\n"); break; + case PNG_TRUECOLOR: printf("truecolor\n"); break; + case PNG_INDEXED: printf("palette\n"); break; + case PNG_GREYSCALE_ALPHA: printf("greyscale with alpha\n"); break; + case PNG_TRUECOLOR_ALPHA: printf("truecolor with alpha\n"); break; + default: printf("unknown, this is not good\n"); break; + } + + printf("\tcompression:\t%s\n", png->compression_method?"unknown, this is not good":"inflate/deflate"); + printf("\tfilter:\t\t%s\n", png->filter_method?"unknown, this is not good":"adaptive"); + printf("\tinterlace:\t%s\n", png->interlace_method?"interlace":"no interlace"); +} + +int png_open_read(png_t* png, png_read_callback_t read_fun, void* user_pointer) +{ + char header[8]; + int result; + + png->read_fun = read_fun; + png->write_fun = 0; + png->user_pointer = user_pointer; + + if(!read_fun && !user_pointer) + return PNG_WRONG_ARGUMENTS; + + if(file_read(png, header, 1, 8) != 8) + return PNG_EOF_ERROR; + + if(memcmp(header, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) != 0) + return PNG_HEADER_ERROR; + + result = png_read_ihdr(png); + + png->bpp = (unsigned char)png_get_bpp(png); + + return result; +} + +int png_open_write(png_t* png, png_write_callback_t write_fun, void* user_pointer) +{ + png->write_fun = write_fun; + png->read_fun = 0; + png->user_pointer = user_pointer; + + if(!write_fun && !user_pointer) + return PNG_WRONG_ARGUMENTS; + + return PNG_NO_ERROR; +} + +int png_open(png_t* png, png_read_callback_t read_fun, void* user_pointer) +{ + return png_open_read(png, read_fun, user_pointer); +} + +int png_open_file_read(png_t *png, const char* filename) +{ + FILE* fp = fopen(filename, "rb"); + + if(!fp) + return PNG_FILE_ERROR; + + return png_open_read(png, 0, fp); +} + +int png_open_file_write(png_t *png, const char* filename) +{ + FILE* fp = fopen(filename, "wb"); + + if(!fp) + return PNG_FILE_ERROR; + + return png_open_write(png, 0, fp); +} + +int png_open_file(png_t *png, const char* filename) +{ + return png_open_file_read(png, filename); +} + +int png_close_file(png_t* png) +{ + fclose(png->user_pointer); + + return PNG_NO_ERROR; +} + +static int png_init_deflate(png_t* png, unsigned char* data, int datalen) +{ + z_stream *stream; + png->zs = png_alloc(sizeof(z_stream)); + + stream = png->zs; + + if(!stream) + return PNG_MEMORY_ERROR; + + memset(stream, 0, sizeof(z_stream)); + + if(deflateInit(stream, Z_DEFAULT_COMPRESSION) != Z_OK) + return PNG_ZLIB_ERROR; + + stream->next_in = data; + stream->avail_in = datalen; + + return PNG_NO_ERROR; +} + +static int png_init_inflate(png_t* png) +{ +#if USE_ZLIB + z_stream *stream; + png->zs = png_alloc(sizeof(z_stream)); +#else + zl_stream *stream; + png->zs = png_alloc(sizeof(zl_stream)); +#endif + + stream = png->zs; + + if(!stream) + return PNG_MEMORY_ERROR; + +#if USE_ZLIB + memset(stream, 0, sizeof(z_stream)); + if(inflateInit(stream) != Z_OK) + return PNG_ZLIB_ERROR; +#else + memset(stream, 0, sizeof(zl_stream)); + if(z_inflateInit(stream) != Z_OK) + return PNG_ZLIB_ERROR; +#endif + + stream->next_out = png->png_data; + stream->avail_out = png->png_datalen; + + return PNG_NO_ERROR; +} + +static int png_end_deflate(png_t* png) +{ + z_stream *stream = png->zs; + + if(!stream) + return PNG_MEMORY_ERROR; + + deflateEnd(stream); + + png_free(png->zs); + + return PNG_NO_ERROR; +} + +static int png_end_inflate(png_t* png) +{ +#if USE_ZLIB + z_stream *stream = png->zs; +#else + zl_stream *stream = png->zs; +#endif + + if(!stream) + return PNG_MEMORY_ERROR; + +#if USE_ZLIB + if(inflateEnd(stream) != Z_OK) +#else + if(z_inflateEnd(stream) != Z_OK) +#endif + { + printf("ZLIB says: %s\n", stream->msg); + return PNG_ZLIB_ERROR; + } + + png_free(png->zs); + + return PNG_NO_ERROR; +} + +static int png_inflate(png_t* png, unsigned char* data, int len) +{ + int result; +#if USE_ZLIB + z_stream *stream = png->zs; +#else + zl_stream *stream = png->zs; +#endif + + if(!stream) + return PNG_MEMORY_ERROR; + + stream->next_in = data; + stream->avail_in = len; + +#if USE_ZLIB + result = inflate(stream, Z_SYNC_FLUSH); +#else + result = z_inflate(stream); +#endif + + if(result != Z_STREAM_END && result != Z_OK) + { + printf("%s\n", stream->msg); + return PNG_ZLIB_ERROR; + } + + if(stream->avail_in != 0) + return PNG_ZLIB_ERROR; + + return PNG_NO_ERROR; +} + +static int png_deflate(png_t* png, char* outdata, int outlen, int *outwritten) +{ + int result; + + z_stream *stream = png->zs; + + if(!stream) + return PNG_MEMORY_ERROR; + + stream->next_out = (unsigned char*)outdata; + stream->avail_out = outlen; + + result = deflate(stream, Z_SYNC_FLUSH); + + *outwritten = outlen - stream->avail_out; + + if(result != Z_STREAM_END && result != Z_OK) + { + printf("%s\n", stream->msg); + return PNG_ZLIB_ERROR; + } + + return result; +} + +static int png_write_idats(png_t* png, unsigned char* data) +{ + unsigned char *chunk; + unsigned long written; + unsigned long crc; + unsigned size = png->width * png->height * png->bpp + png->height; + unsigned chunk_size = compressBound(size); + + (void)png_init_deflate; + (void)png_end_deflate; + (void)png_deflate; + + chunk = png_alloc(chunk_size + 4); + memcpy(chunk, "IDAT", 4); + + written = chunk_size; + compress(chunk+4, &written, data, size); + + crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, chunk, written+4); + set_ul(chunk+written+4, crc); + file_write_ul(png, written); + file_write(png, chunk, 1, written+8); + png_free(chunk); + + file_write_ul(png, 0); + file_write(png, "IEND", 1, 4); + crc = crc32(0L, (const unsigned char *)"IEND", 4); + file_write_ul(png, crc); + + return PNG_NO_ERROR; +} + +static int png_read_idat(png_t* png, unsigned length) +{ +#if DO_CRC_CHECKS + unsigned orig_crc; + unsigned calc_crc; +#endif + + if(!png->readbuf || png->readbuflen < length) + { + if (png->readbuf) + { + png_free(png->readbuf); + } + png->readbuf = png_alloc(length); + png->readbuflen = length; + } + + if(!png->readbuf) + { + return PNG_MEMORY_ERROR; + } + + if(file_read(png, png->readbuf, 1, length) != length) + { + return PNG_FILE_ERROR; + } + +#if DO_CRC_CHECKS + calc_crc = crc32(0L, Z_NULL, 0); + calc_crc = crc32(calc_crc, (unsigned char*)"IDAT", 4); + calc_crc = crc32(calc_crc, (unsigned char*)png->readbuf, length); + + file_read_ul(png, &orig_crc); + + if(orig_crc != calc_crc) + { + return PNG_CRC_ERROR; + } +#else + file_read_ul(png); +#endif + + return png_inflate(png, png->readbuf, length); +} + +static int png_process_chunk(png_t* png) +{ + int result = PNG_NO_ERROR; + unsigned type; + unsigned length; + + file_read_ul(png, &length); + + if(file_read(png, &type, 1, 4) != 4) + return PNG_FILE_ERROR; + + if(type == *(unsigned int*)"IDAT") /* if we found an idat, all other idats should be followed with no other chunks in between */ + { + if(!png->png_data) /* first IDAT */ + { + png->png_datalen = png->width * png->height * png->bpp + png->height; + png->png_data = png_alloc(png->png_datalen); + } + + if(!png->png_data) + return PNG_MEMORY_ERROR; + + if(!png->zs) + { + result = png_init_inflate(png); + if(result != PNG_NO_ERROR) + return result; + } + + return png_read_idat(png, length); + } + else if(type == *(unsigned int*)"IEND") + { + return PNG_DONE; + } + else + { + file_read(png, 0, 1, length + 4); /* unknown chunk */ + } + + return result; +} + +static void png_filter_sub(int stride, unsigned char* in, unsigned char* out, int len) +{ + int i; + unsigned char a = 0; + + for(i = 0; i < len; i++) + { + if(i >= stride) + a = out[i - stride]; + + out[i] = in[i] + a; + } +} + +static void png_filter_up(int stride, unsigned char* in, unsigned char* out, unsigned char* prev_line, int len) +{ + int i; + + if(prev_line) + { + for(i = 0; i < len; i++) + out[i] = in[i] + prev_line[i]; + } + else + memcpy(out, in, len); +} + +static void png_filter_average(int stride, unsigned char* in, unsigned char* out, unsigned char* prev_line, int len) +{ + int i; + unsigned char a = 0; + unsigned char b = 0; + unsigned int sum = 0; + + for(i = 0; i < len; i++) + { + if(prev_line) + b = prev_line[i]; + + if(i >= stride) + a = out[i - stride]; + + sum = a; + sum += b; + + out[i] = (char)(in[i] + sum/2); + } +} + +static unsigned char png_paeth(unsigned char a, unsigned char b, unsigned char c) +{ + int p = (int)a + b - c; + int pa = abs(p - a); + int pb = abs(p - b); + int pc = abs(p - c); + + int pr; + + if(pa <= pb && pa <= pc) + pr = a; + else if(pb <= pc) + pr = b; + else + pr = c; + + return (char)pr; +} + +static void png_filter_paeth(int stride, unsigned char* in, unsigned char* out, unsigned char* prev_line, int len) +{ + int i; + unsigned char a; + unsigned char b; + unsigned char c; + + for(i = 0; i < len; i++) + { + if(prev_line && i >= stride) + { + a = out[i - stride]; + b = prev_line[i]; + c = prev_line[i - stride]; + } + else + { + if(prev_line) + b = prev_line[i]; + else + b = 0; + + if(i >= stride) + a = out[i - stride]; + else + a = 0; + + c = 0; + } + + out[i] = in[i] + png_paeth(a, b, c); + } +} + +static int png_filter(png_t* png, unsigned char* data) +{ + return PNG_NO_ERROR; +} + +static int png_unfilter(png_t* png, unsigned char* data) +{ + unsigned i; + unsigned pos = 0; + unsigned outpos = 0; + unsigned char *filtered = png->png_data; + + int stride = png->bpp; + + while(pos < png->png_datalen) + { + unsigned char filter = filtered[pos]; + + pos++; + + if(png->depth == 16) + { + for(i = 0; i < png->width * stride; i+=2) + { + *(short*)(filtered+pos+i) = (filtered[pos+i] << 8) | filtered[pos+i+1]; + } + } + + switch(filter) + { + case 0: /* none */ + memcpy(data+outpos, filtered+pos, png->width * stride); + break; + case 1: /* sub */ + png_filter_sub(stride, filtered+pos, data+outpos, png->width * stride); + break; + case 2: /* up */ + if(outpos) + png_filter_up(stride, filtered+pos, data+outpos, data + outpos - (png->width*stride), png->width*stride); + else + png_filter_up(stride, filtered+pos, data+outpos, 0, png->width*stride); + break; + case 3: /* average */ + if(outpos) + png_filter_average(stride, filtered+pos, data+outpos, data + outpos - (png->width*stride), png->width*stride); + else + png_filter_average(stride, filtered+pos, data+outpos, 0, png->width*stride); + break; + case 4: /* paeth */ + if(outpos) + png_filter_paeth(stride, filtered+pos, data+outpos, data + outpos - (png->width*stride), png->width*stride); + else + png_filter_paeth(stride, filtered+pos, data+outpos, 0, png->width*stride); + break; + default: + return PNG_UNKNOWN_FILTER; + } + + outpos += png->width * stride; + pos += png->width * stride; + } + + return PNG_NO_ERROR; +} + +int png_get_data(png_t* png, unsigned char* data) +{ + int result = PNG_NO_ERROR; + + png->zs = NULL; + png->png_datalen = 0; + png->png_data = NULL; + png->readbuf = NULL; + png->readbuflen = 0; + + while(result == PNG_NO_ERROR) + { + result = png_process_chunk(png); + } + + if (png->readbuf) + { + png_free(png->readbuf); + png->readbuflen = 0; + } + if (png->zs) + { + png_end_inflate(png); + } + + if(result != PNG_DONE) + { + png_free(png->png_data); + return result; + } + + result = png_unfilter(png, data); + + png_free(png->png_data); + + return result; +} + +int png_set_data(png_t* png, unsigned width, unsigned height, char depth, int color, unsigned char* data) +{ + int i; + unsigned char *filtered; + png->width = width; + png->height = height; + png->depth = depth; + png->color_type = color; + png->bpp = png_get_bpp(png); + + filtered = png_alloc(width * height * png->bpp + height); + + for(i = 0; i < png->height; i++) + { + filtered[i*png->width*png->bpp+i] = 0; + memcpy(&filtered[i*png->width*png->bpp+i+1], data + i * png->width*png->bpp, png->width*png->bpp); + } + + png_filter(png, filtered); + png_write_ihdr(png); + png_write_idats(png, filtered); + + png_free(filtered); + + return PNG_NO_ERROR; +} + +char* png_error_string(int error) +{ + switch(error) + { + case PNG_NO_ERROR: + return "No error"; + case PNG_FILE_ERROR: + return "Unknown file error."; + case PNG_HEADER_ERROR: + return "No PNG header found. Are you sure this is a PNG?"; + case PNG_IO_ERROR: + return "Failure while reading file."; + case PNG_EOF_ERROR: + return "Reached end of file."; + case PNG_CRC_ERROR: + return "CRC or chunk length error."; + case PNG_MEMORY_ERROR: + return "Could not allocate memory."; + case PNG_ZLIB_ERROR: + return "zlib reported an error."; + case PNG_UNKNOWN_FILTER: + return "Unknown filter method used in scanline."; + case PNG_DONE: + return "PNG done"; + case PNG_NOT_SUPPORTED: + return "The PNG is unsupported by pnglite, too bad for you!"; + case PNG_WRONG_ARGUMENTS: + return "Wrong combination of arguments passed to png_open. You must use either a read_function or supply a file pointer to use."; + default: + return "Unknown error."; + }; +} diff --git a/src/pnglite.h b/src/pnglite.h new file mode 100644 index 0000000..bbb2075 --- /dev/null +++ b/src/pnglite.h @@ -0,0 +1,232 @@ +/* pnglite.h - Interface for pnglite library + Copyright (c) 2007 Daniel Karling + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + Daniel Karling + daniel.karling@gmail.com + */ + + +#ifndef _PNGLITE_H_ +#define _PNGLITE_H_ + +#include + +#ifdef __cplusplus +extern "C"{ +#endif + +/* + Enumerations for pnglite. + Negative numbers are error codes and 0 and up are okay responses. +*/ + +enum +{ + PNG_DONE = 1, + PNG_NO_ERROR = 0, + PNG_FILE_ERROR = -1, + PNG_HEADER_ERROR = -2, + PNG_IO_ERROR = -3, + PNG_EOF_ERROR = -4, + PNG_CRC_ERROR = -5, + PNG_MEMORY_ERROR = -6, + PNG_ZLIB_ERROR = -7, + PNG_UNKNOWN_FILTER = -8, + PNG_NOT_SUPPORTED = -9, + PNG_WRONG_ARGUMENTS = -10 +}; + +/* + The five different kinds of color storage in PNG files. +*/ + +enum +{ + PNG_GREYSCALE = 0, + PNG_TRUECOLOR = 2, + PNG_INDEXED = 3, + PNG_GREYSCALE_ALPHA = 4, + PNG_TRUECOLOR_ALPHA = 6 +}; + +/* + Typedefs for callbacks. +*/ + +typedef unsigned (*png_write_callback_t)(void* input, size_t size, size_t numel, void* user_pointer); +typedef unsigned (*png_read_callback_t)(void* output, size_t size, size_t numel, void* user_pointer); +typedef void (*png_free_t)(void* p); +typedef void * (*png_alloc_t)(size_t s); + +typedef struct +{ + void* zs; /* pointer to z_stream */ + png_read_callback_t read_fun; + png_write_callback_t write_fun; + void* user_pointer; + + unsigned char* png_data; + unsigned png_datalen; + + unsigned width; + unsigned height; + unsigned char depth; + unsigned char color_type; + unsigned char compression_method; + unsigned char filter_method; + unsigned char interlace_method; + unsigned char bpp; + + unsigned char* readbuf; + unsigned readbuflen; +} png_t; + +/* + Function: png_init + + This function initializes pnglite. The parameters can be used to set your own memory allocation routines following these formats: + + > void* (*custom_alloc)(size_t s) + > void (*custom_free)(void* p) + Parameters: + pngalloc - Pointer to custom allocation routine. If 0 is passed, malloc from libc will be used. + pngfree - Pointer to custom free routine. If 0 is passed, free from libc will be used. + + Returns: + Always returns PNG_NO_ERROR. +*/ + +int png_init(png_alloc_t pngalloc, png_free_t pngfree); + +/* + Function: png_open_file + + This function is used to open a png file with the internal file IO system. This function should be used instead of + png_open if no custom read function is used. + + Parameters: + png - Empty png_t struct. + filename - Filename of the file to be opened. + + Returns: + PNG_NO_ERROR on success, otherwise an error code. +*/ + +int png_open_file(png_t *png, const char* filename); + +int png_open_file_read(png_t *png, const char* filename); +int png_open_file_write(png_t *png, const char* filename); + +/* + Function: png_open + + This function reads or writes a png from/to the specified callback. The callbacks should be of the format: + + > size_t (*png_write_callback_t)(void* input, size_t size, size_t numel, void* user_pointer); + > size_t (*png_read_callback_t)(void* output, size_t size, size_t numel, void* user_pointer). + + Only one callback has to be specified. The read callback in case of PNG reading, otherwise the write callback. + + Writing: + The callback will be called like fwrite. + + Reading: + The callback will be called each time pnglite needs more data. The callback should read as much data as requested, + or return 0. This should always be possible if the PNG is sane. If the output-buffer is a null-pointer the callback + should only skip ahead the specified number of elements. If the callback is a null-pointer the user_pointer will be + treated as a file pointer (use png_open_file instead). + + Parameters: + png - png_t struct + read_fun - Callback function for reading. + user_pointer - User pointer to be passed to read_fun. + + Returns: + PNG_NO_ERROR on success, otherwise an error code. +*/ + +int png_open(png_t* png, png_read_callback_t read_fun, void* user_pointer); + +int png_open_read(png_t* png, png_read_callback_t read_fun, void* user_pointer); +int png_open_write(png_t* png, png_write_callback_t write_fun, void* user_pointer); + +/* + Function: png_print_info + + This function prints some info about the opened png file to stdout. + + Parameters: + png - png struct to get info from. +*/ + +void png_print_info(png_t* png); + +/* + Function: png_error_string + + This function translates an error code to a human readable string. + + Parameters: + error - Error code. + + Returns: + Pointer to string. +*/ + +char* png_error_string(int error); + +/* + Function: png_get_data + + This function decodes the opened png file and stores the result in data. data should be big enough to hold the decoded png. Required size will be: + + > width*height*(bytes per pixel) + + Parameters: + data - Where to store result. + + Returns: + PNG_NO_ERROR on success, otherwise an error code. +*/ + +int png_get_data(png_t* png, unsigned char* data); + +int png_set_data(png_t* png, unsigned width, unsigned height, char depth, int color, unsigned char* data); + +/* + Function: png_close_file + + Closes an open png file pointer. Should only be used when the png has been opened with png_open_file. + + Parameters: + png - png to close. + + Returns: + PNG_NO_ERROR +*/ + +int png_close_file(png_t* png); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/sram.c b/src/sram.c index 661d95b..e03ae19 100644 --- a/src/sram.c +++ b/src/sram.c @@ -19,7 +19,7 @@ cammy_sram *cammy_sram_open(const char *file) { goto error_malloc_sram; } - if ((sram->fd = open(file, O_RDONLY)) < 0) { + if ((sram->fd = open(file, O_RDWR)) < 0) { goto error_open; } diff --git a/src/test.c b/src/test.c index bb97136..22c978e 100644 --- a/src/test.c +++ b/src/test.c @@ -3,6 +3,7 @@ #include #include #include +#include "pnglite.h" #include @@ -16,16 +17,61 @@ static void usage(int argc, char **argv, const char *message, ...) { fprintf(stderr, "\n"); } - fprintf(stderr, "usage: %s file.sav\n", argv[0]); + fprintf(stderr, "usage: %s file.sav frame1..30 photo.png\n", argv[0]); exit(1); } +static uint8_t *buf_malloc(png_t *png) { + return malloc(4 * png->width * png->height); +} + +static void image_copy(cammy_sram_frame *frame, uint8_t *buf) { + size_t x, y; + + memset(&frame->photo, '\0', sizeof(cammy_sram_photo)); + + for (y=0; yphoto.tiles[y>>3][x>>3]; + + int tile_x = x & 7, + tile_y = y & 7; + + tile->data[2*tile_y] |= ((value & 0x02) >> 1) << (7 - tile_x); + tile->data[2*tile_y+1] |= (value & 0x01) << (7 - tile_x); + } + } +} + int main(int argc, char **argv) { cammy_sram *sram; + png_t *png; + int frame = 0; + uint8_t *buf; - if (argc != 2) { + int error; + + if (argc < 2) { usage(argc, argv, "No save file provided"); + } else if (argc < 3) { + usage(argc, argv, "No frame number provided"); + } else if (argc < 4) { + usage(argc, argv, "No photo provided"); + } + + frame = atoi(argv[2]); + + if (frame < 1 || frame > CAMMY_SRAM_FRAME_COUNT) { + usage(argc, argv, "Invalid frame number"); } if ((sram = cammy_sram_open(argv[1])) == NULL) { @@ -35,10 +81,66 @@ int main(int argc, char **argv) { goto error_sram_open; } + png_init(malloc, free); + + if ((png = malloc(sizeof(*png))) == NULL) { + goto error_malloc_png; + } + + if ((error = png_open_file_read(png, argv[3])) < 0) { + fprintf(stderr, "%s: %s: %s: %s\n", + argv[0], "png_open_file_read()", argv[3], png_error_string(error)); + + goto error_png_open_file_read; + } + + if (png->width != CAMMY_SRAM_PHOTO_WIDTH + || png->height != CAMMY_SRAM_PHOTO_HEIGHT) { + fprintf(stderr, "%s: %s: %s\n", + argv[0], argv[3], "Invalid image dimensions"); + + goto error_invalid_dimensions; + } + + if ((buf = buf_malloc(png)) == NULL) { + fprintf(stderr, "%s: %s: %s\n", + argv[0], "malloc()", strerror(errno)); + + goto error_buf_malloc; + } + + if ((error = png_get_data(png, buf)) < 0) { + fprintf(stderr, "%s: %s: %s: %s\n", + argv[0], "png_get_data()", argv[3], png_error_string(error)); + + goto error_png_get_data; + } + + image_copy(&sram->data->frames[frame], buf); + + free(buf); + + png_close_file(png); + + free(png); + cammy_sram_close(sram); return 0; +error_png_get_data: + free(buf); + +error_buf_malloc: +error_invalid_dimensions: + png_close_file(png); + +error_png_open_file_read: + free(png); + +error_malloc_png: + cammy_sram_close(sram); + error_sram_open: return 1; }