this is starting to hurt my head

This commit is contained in:
XANTRONIX Development 2021-11-29 01:18:39 -05:00
parent a9c9ef12c9
commit 8330736b1e
7 changed files with 1253 additions and 47 deletions

View file

@ -11,6 +11,9 @@
#define CAMMY_IMAGE_CHUNK_SIZE 4096 #define CAMMY_IMAGE_CHUNK_SIZE 4096
#define CAMMY_IMAGE_MAGIC_TILE "TILE"
#define CAMMY_IMAGE_MAGIC_PNG "\x89PNG"
typedef enum { typedef enum {
CAMMY_IMAGE_NONE, CAMMY_IMAGE_NONE,
CAMMY_IMAGE_2BPP_TILE, CAMMY_IMAGE_2BPP_TILE,
@ -29,8 +32,18 @@ typedef struct _cammy_image {
uint8_t *buf; uint8_t *buf;
cammy_tile *tiles; cammy_tile *tiles;
}; };
char *file;
} cammy_image; } cammy_image;
#define CAMMY_IMAGE_HEADER_VERSION 0x0001
typedef struct _cammy_image_header {
uint8_t magic[4];
uint16_t version;
uint16_t width, height;
} cammy_image_header;
typedef struct _cammy_image_point { typedef struct _cammy_image_point {
size_t x, y; size_t x, y;
} cammy_image_point; } cammy_image_point;
@ -43,25 +56,12 @@ cammy_image *cammy_image_new(cammy_image_format format,
size_t width, size_t width,
size_t height); size_t height);
void cammy_image_close(cammy_image *image); cammy_image *cammy_image_open(const char *filename);
void cammy_image_destroy(cammy_image *image); void cammy_image_destroy(cammy_image *image);
cammy_image *cammy_image_open_tile(const char *filename,
size_t width,
size_t height);
int cammy_image_save_tile(cammy_image *image, const char *filename);
int cammy_image_save(cammy_image *image, const char *filename); int cammy_image_save(cammy_image *image, const char *filename);
void cammy_image_dither(uint8_t *dest,
uint8_t *src,
size_t width,
size_t height,
int depth,
cammy_tile_palette *palette);
void cammy_image_split_to_tiles(cammy_tile *destr, void cammy_image_split_to_tiles(cammy_tile *destr,
cammy_tile *destg, cammy_tile *destg,
cammy_tile *destb, cammy_tile *destb,
@ -70,13 +70,6 @@ void cammy_image_split_to_tiles(cammy_tile *destr,
size_t height, size_t height,
int depth); int depth);
void cammy_image_copy_from_tile(uint8_t *dest,
cammy_tile *src,
size_t width,
size_t height,
int depth,
cammy_tile_palette *palette);
void cammy_image_merge_tiles(uint8_t *dest, void cammy_image_merge_tiles(uint8_t *dest,
cammy_tile *srcr, cammy_tile *srcr,
cammy_tile *srcg, cammy_tile *srcg,

View file

@ -37,9 +37,8 @@ typedef struct _cammy_photo {
#pragma pack(pop) #pragma pack(pop)
void cammy_photo_export(cammy_photo *src, cammy_image *cammy_photo_export(cammy_photo *src,
uint8_t *dest, cammy_image_format format,
int depth,
cammy_tile_palette *palette); cammy_tile_palette *palette);
void cammy_photo_merge(cammy_photo *srcr, void cammy_photo_merge(cammy_photo *srcr,

View file

@ -7,12 +7,12 @@ CC = $(CROSS)cc
CFLAGS = $(CGFLAGS) -fPIC -Wall -O2 -I$(INCLUDE_PATH) CFLAGS = $(CGFLAGS) -fPIC -Wall -O2 -I$(INCLUDE_PATH)
LDFLAGS = -lz LDFLAGS = -lz
HEADERS_LOCAL = HEADERS_LOCAL = pnglite.h
HEADERS_BUILD = $(HEADERS_LOCAL) \ HEADERS_BUILD = $(HEADERS_LOCAL) \
$(addprefix $(INCLUDE_PATH)/$(HEADER_SUBDIR)/,$(HEADERS)) $(addprefix $(INCLUDE_PATH)/$(HEADER_SUBDIR)/,$(HEADERS))
HEADERS = sram.h image.h photo.h HEADERS = sram.h image.h photo.h
OBJS = sram.o image.o photo.o OBJS = sram.o image.o photo.o pnglite.o
VERSION_MAJOR = 0 VERSION_MAJOR = 0
VERSION_MINOR = 0.1 VERSION_MINOR = 0.1

View file

@ -10,6 +10,8 @@
#include <cammy/screen.h> #include <cammy/screen.h>
#include <cammy/image.h> #include <cammy/image.h>
#include "pnglite.h"
static uint32_t bayer_matrix[256] = { static uint32_t bayer_matrix[256] = {
/* /*
* Black gamut * Black gamut
@ -158,8 +160,6 @@ cammy_image *cammy_image_new(cammy_image_format format,
size_t width, size_t width,
size_t height) { size_t height) {
cammy_image *image; cammy_image *image;
uint8_t *buf;
size_t size; size_t size;
switch (format) { switch (format) {
@ -188,15 +188,15 @@ cammy_image *cammy_image_new(cammy_image_format format,
goto error_malloc_image; goto error_malloc_image;
} }
if ((buf = malloc(size)) == NULL) { if ((image->buf = malloc(size)) == NULL) {
goto error_malloc_buf; goto error_malloc_buf;
} }
image->buf = buf;
image->format = format; image->format = format;
image->size = size; image->size = size;
image->width = width; image->width = width;
image->height = height; image->height = height;
image->file = NULL;
return image; return image;
@ -208,28 +208,84 @@ error_invalid_format:
return NULL; return NULL;
} }
void cammy_image_close(cammy_image *image) {
free(image);
}
void cammy_image_destroy(cammy_image *image) { void cammy_image_destroy(cammy_image *image) {
free(image->buf); free(image->buf);
free(image); free(image);
} }
cammy_image *cammy_image_open_tile(const char *filename, static cammy_image *load_png(const char *file) {
size_t width, png_t png;
size_t height) {
cammy_image *image;
int fd;
cammy_image *image;
cammy_image_format format = CAMMY_IMAGE_NONE;
png_init(malloc, free);
if (png_open_file_read(&png, file) < 0) {
goto error_png_open_file_read;
}
switch (png.bpp) {
case 3:
format = CAMMY_IMAGE_24BPP_RGB;
break;
case 4:
format = CAMMY_IMAGE_32BPP_RGBA;
break;
default:
errno = EINVAL;
goto error_invalid_format;
}
if ((image = cammy_image_new(format, png.width, png.height)) == NULL) {
goto error_image_new;
}
if (png_get_data(&png, image->buf) < 0) {
goto error_png_get_data;
}
png_close_file(&png);
return image;
error_png_get_data:
cammy_image_destroy(image);
error_image_new:
error_invalid_format:
png_close_file(&png);
error_png_open_file_read:
return NULL;
}
static cammy_image *load_tile(const char *filename) {
int fd;
size_t off; size_t off;
cammy_image *image;
cammy_image_header header;
if ((fd = open(filename, O_RDONLY)) < 0) { if ((fd = open(filename, O_RDONLY)) < 0) {
goto error_open; goto error_open;
} }
if ((image = cammy_image_new(CAMMY_IMAGE_2BPP_TILE, width, height)) == NULL) { if (read(fd, &header, sizeof(header)) < 0) {
goto error_read_header;
}
if (be16toh(header.version) != CAMMY_IMAGE_HEADER_VERSION) {
errno = EINVAL;
goto error_invalid_version;
}
if ((image = cammy_image_new(CAMMY_IMAGE_2BPP_TILE,
be16toh(header.width),
be16toh(header.height))) == NULL) {
goto error_image_new; goto error_image_new;
} }
@ -251,22 +307,81 @@ error_read:
cammy_image_destroy(image); cammy_image_destroy(image);
error_image_new: error_image_new:
error_invalid_version:
error_read_header:
close(fd); close(fd);
error_open: error_open:
return NULL; return NULL;
} }
int cammy_image_save_tile(cammy_image *image, const char *filename) { cammy_image *cammy_image_open(const char *file) {
cammy_image *image;
int fd;
uint8_t buf[4];
cammy_image *(*loader)(const char *) = NULL;
if ((fd = open(file, O_RDONLY)) < 0) {
goto error_open;
}
if (lseek(fd, 0, SEEK_SET) < 0) {
goto error_lseek;
}
if (read(fd, buf, sizeof(buf)) < 0) {
goto error_read;
}
close(fd);
if (memcmp(buf, CAMMY_IMAGE_MAGIC_PNG, sizeof(buf)) == 0) {
loader = load_png;
} else if (memcmp(buf, CAMMY_IMAGE_MAGIC_TILE, sizeof(buf)) == 0) {
loader = load_tile;
} else {
goto error_invalid_format;
}
if ((image = loader(file)) == NULL) {
goto error_load_image;
}
return image;
error_load_image:
error_invalid_format:
error_read:
error_lseek:
close(fd);
error_open:
return NULL;
}
int cammy_image_save(cammy_image *image, const char *filename) {
size_t off, size_t off,
rest = image->size; rest = image->size;
int fd; int fd;
cammy_image_header header = {
.magic = CAMMY_IMAGE_MAGIC_TILE,
.version = htobe16(CAMMY_IMAGE_HEADER_VERSION),
.width = htobe16(image->width),
.height = htobe16(image->height)
};
if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0) { if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0) {
goto error_open; goto error_open;
} }
if (write(fd, &header, sizeof(header)) < 0) {
goto error_write;
}
for (off=0; off<image->size; off+=CAMMY_IMAGE_CHUNK_SIZE) { for (off=0; off<image->size; off+=CAMMY_IMAGE_CHUNK_SIZE) {
size_t chunk = rest > CAMMY_IMAGE_CHUNK_SIZE? size_t chunk = rest > CAMMY_IMAGE_CHUNK_SIZE?
CAMMY_IMAGE_CHUNK_SIZE: rest; CAMMY_IMAGE_CHUNK_SIZE: rest;
@ -532,7 +647,10 @@ int cammy_image_slice(uint8_t *buf,
size_t x, y; size_t x, y;
if ((dest = cammy_image_new(CAMMY_IMAGE_2BPP_TILE, width, height)) == NULL) { if ((dest = cammy_image_new(CAMMY_IMAGE_2BPP_TILE,
width,
height
)) == NULL) {
goto error_image_new; goto error_image_new;
} }

View file

@ -5,10 +5,12 @@
#include <cammy/image.h> #include <cammy/image.h>
#include <cammy/photo.h> #include <cammy/photo.h>
void cammy_photo_export(cammy_photo *src, cammy_image *cammy_photo_export(cammy_photo *src,
uint8_t *dest, cammy_image_format format,
int depth,
cammy_tile_palette *palette) { cammy_tile_palette *palette) {
cammy_image *image;
if ((image = cammy_image_))
cammy_image_copy_from_tile(dest, cammy_image_copy_from_tile(dest,
(cammy_tile *)&src->tiles, (cammy_tile *)&src->tiles,
CAMMY_PHOTO_WIDTH, CAMMY_PHOTO_WIDTH,

862
src/pnglite.c Normal file
View file

@ -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 <zlib.h>
#else
#include "zlite.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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.";
};
}

232
src/pnglite.h Normal file
View file

@ -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 <string.h>
#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