this is starting to hurt my head
This commit is contained in:
parent
a9c9ef12c9
commit
8330736b1e
7 changed files with 1253 additions and 47 deletions
|
@ -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,
|
||||||
|
|
|
@ -37,10 +37,9 @@ 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,
|
||||||
cammy_photo *srcg,
|
cammy_photo *srcg,
|
||||||
|
|
|
@ -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
|
||||||
|
|
150
src/image.c
150
src/image.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
src/photo.c
10
src/photo.c
|
@ -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
862
src/pnglite.c
Normal 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
232
src/pnglite.h
Normal 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
|
Loading…
Add table
Reference in a new issue