#include "impng.h"
#include "png.h"
+#include <stdlib.h>
/* this is a way to get number of channels from color space
* Color code to channel number */
static i_img *
read_bilevel(png_structp png_ptr, png_infop info_ptr, i_img_dim width, i_img_dim height);
+static int
+write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im);
+
+static int
+write_direct16(png_structp png_ptr, png_infop info_ptr, i_img *im);
+
+static int
+write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits);
+
+static int
+write_bilevel(png_structp png_ptr, png_infop info_ptr, i_img *im);
+
unsigned
i_png_lib_version(void) {
return png_access_version_number();
static void
wiol_flush_data(png_structp png_ptr) {
- /* XXX : This needs to be added to the io layer */
+ io_glue *ig = png_get_io_ptr(png_ptr);
+ if (!i_io_flush(ig))
+ png_error(png_ptr, "Error flushing output");
}
static void
int aspect_only, have_res;
unsigned char *data;
unsigned char * volatile vdata = NULL;
+ int bits;
+ int is_bilevel = 0, zero_is_white;
mm_log((1,"i_writepng(im %p ,ig %p)\n", im, ig));
channels=im->channels;
- if (channels > 2) { cspace = PNG_COLOR_TYPE_RGB; channels-=3; }
- else { cspace=PNG_COLOR_TYPE_GRAY; channels--; }
-
- if (channels) cspace|=PNG_COLOR_MASK_ALPHA;
- mm_log((1,"cspace=%d\n",cspace));
+ if (i_img_is_monochrome(im, &zero_is_white)) {
+ is_bilevel = 1;
+ bits = 1;
+ cspace = PNG_COLOR_TYPE_GRAY;
+ mm_log((1, "i_writepng: bilevel output\n"));
+ }
+ else if (im->type == i_palette_type) {
+ int colors = i_colorcount(im);
+
+ cspace = PNG_COLOR_TYPE_PALETTE;
+ bits = 1;
+ while ((1 << bits) < colors) {
+ bits += bits;
+ }
+ mm_log((1, "i_writepng: paletted output\n"));
+ }
+ else {
+ switch (channels) {
+ case 1:
+ cspace = PNG_COLOR_TYPE_GRAY;
+ break;
+ case 2:
+ cspace = PNG_COLOR_TYPE_GRAY_ALPHA;
+ break;
+ case 3:
+ cspace = PNG_COLOR_TYPE_RGB;
+ break;
+ case 4:
+ cspace = PNG_COLOR_TYPE_RGB_ALPHA;
+ break;
+ default:
+ fprintf(stderr, "Internal error, channels = %d\n", channels);
+ abort();
+ }
+ bits = im->bits > 8 ? 16 : 8;
+ mm_log((1, "i_writepng: direct output\n"));
+ }
- channels = im->channels;
+ mm_log((1,"i_writepng: cspace=%d, bits=%d\n",cspace, bits));
/* Create and initialize the png_struct with the desired error handler
* functions. If you want to use the default stderr and longjump method,
*/
png_set_user_limits(png_ptr, width, height);
- png_set_IHDR(png_ptr, info_ptr, width, height, 8, cspace,
+ mm_log((1, ">png_set_IHDR\n"));
+ png_set_IHDR(png_ptr, info_ptr, width, height, bits, cspace,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ mm_log((1, "<png_set_IHDR\n"));
have_res = 1;
if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) {
aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER);
}
- png_write_info(png_ptr, info_ptr);
-
- vdata = data = mymalloc(im->xsize * im->channels);
- for (y = 0; y < height; y++) {
- i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels);
- png_write_row(png_ptr, (png_bytep)data);
+ if (is_bilevel) {
+ if (!write_bilevel(png_ptr, info_ptr, im)) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return 0;
+ }
+ }
+ else if (im->type == i_palette_type) {
+ if (!write_paletted(png_ptr, info_ptr, im, bits)) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return 0;
+ }
+ }
+ else if (bits == 16) {
+ if (!write_direct16(png_ptr, info_ptr, im)) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return 0;
+ }
+ }
+ else {
+ if (!write_direct8(png_ptr, info_ptr, im)) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return 0;
+ }
}
- myfree(data);
png_write_end(png_ptr, info_ptr);
png_infop info_ptr;
png_uint_32 width, height;
int bit_depth, color_type, interlace_type;
- int number_passes,y;
- int channels,pass;
+ int channels;
unsigned int sig_read;
i_png_read_state rs;
- i_img_dim wmax, hmax;
- size_t bytes;
rs.warnings = NULL;
sig_read = 0;
read_direct16(png_structp png_ptr, png_infop info_ptr, int channels,
i_img_dim width, i_img_dim height) {
i_img * volatile vim = NULL;
- int color_type = png_get_color_type(png_ptr, info_ptr);
i_img_dim x, y;
int number_passes, pass;
i_img *im;
return im;
}
+struct png_text_name {
+ const char *keyword;
+ const char *tagname;
+};
+
+static const struct png_text_name
+text_tags[] = {
+ { "Author", "png_author" },
+ { "Comment", "i_comment" },
+ { "Copyright", "png_copyright" },
+ { "Creation Time", "png_creation_time" },
+ { "Description", "png_description" },
+ { "Disclaimer", "png_disclaimer" },
+ { "Software", "png_software" },
+ { "Source", "png_source" },
+ { "Title", "png_title" },
+ { "Warning", "png_warning" }
+};
+
+static const int text_tags_count = sizeof(text_tags) / sizeof(*text_tags);
+
static void
get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth) {
png_uint_32 xres, yres;
/* the various readers can call png_set_expand(), libpng will make
it's internal record of bit_depth at least 8 in that case */
i_tags_setn(&im->tags, "png_bits", bit_depth);
+
+
+ {
+ int intent;
+ if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
+ i_tags_setn(&im->tags, "png_srgb_intent", intent);
+ }
+ }
+ {
+ double gamma;
+ if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
+ i_tags_set_float2(&im->tags, "png_gamma", 0, gamma, 4);
+ }
+ }
+ {
+ double white_x, white_y;
+ double red_x, red_y;
+ double green_x, green_y;
+ double blue_x, blue_y;
+ if (png_get_cHRM(png_ptr, info_ptr, &white_x, &white_y,
+ &red_x, &red_y, &green_x, &green_y,
+ &blue_x, &blue_y)) {
+ i_tags_set_float2(&im->tags, "png_chroma_white_x", 0, white_x, 4);
+ i_tags_set_float2(&im->tags, "png_chroma_white_y", 0, white_y, 4);
+ i_tags_set_float2(&im->tags, "png_chroma_red_x", 0, red_x, 4);
+ i_tags_set_float2(&im->tags, "png_chroma_red_y", 0, red_y, 4);
+ i_tags_set_float2(&im->tags, "png_chroma_green_x", 0, green_x, 4);
+ i_tags_set_float2(&im->tags, "png_chroma_green_y", 0, green_y, 4);
+ i_tags_set_float2(&im->tags, "png_chroma_blue_x", 0, blue_x, 4);
+ i_tags_set_float2(&im->tags, "png_chroma_blue_y", 0, blue_y, 4);
+ }
+ }
+
+ {
+ int num_text;
+ png_text *text;
+
+ if (png_get_text(png_ptr, info_ptr, &text, &num_text)) {
+ int i;
+ for (i = 0; i < num_text; ++i) {
+ int j;
+ char tag_name[50];
+ sprintf(tag_name, "png_text%d_key", i);
+ i_tags_set(&im->tags, tag_name, text[i].key, -1);
+ sprintf(tag_name, "png_text%d_text", i);
+ i_tags_set(&im->tags, tag_name, text[i].text, -1);
+ sprintf(tag_name, "png_text%d_type", i);
+ i_tags_set(&im->tags, tag_name,
+ (text[i].compression == PNG_TEXT_COMPRESSION_NONE
+ || text[i].compression == PNG_TEXT_COMPRESSION_zTXt) ?
+ "text" : "itxt", -1);
+
+ for (j = 0; j < text_tags_count; ++j) {
+ if (strcmp(text_tags[j].keyword, text[i].key) == 0) {
+ i_tags_set(&im->tags, text_tags[j].tagname, text[i].text, -1);
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+static int
+write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im) {
+ unsigned char *data, *volatile vdata = NULL;
+ i_img_dim y;
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ if (vdata)
+ myfree(vdata);
+
+ return 0;
+ }
+
+ png_write_info(png_ptr, info_ptr);
+
+ vdata = data = mymalloc(im->xsize * im->channels);
+ for (y = 0; y < im->ysize; y++) {
+ i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels);
+ png_write_row(png_ptr, (png_bytep)data);
+ }
+ myfree(data);
+
+ return 1;
+}
+
+static int
+write_direct16(png_structp png_ptr, png_infop info_ptr, i_img *im) {
+ unsigned *data, *volatile vdata = NULL;
+ unsigned char *tran_data, * volatile vtran_data = NULL;
+ i_img_dim samples_per_row = im->xsize * im->channels;
+
+ i_img_dim y;
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ if (vdata)
+ myfree(vdata);
+ if (vtran_data)
+ myfree(vtran_data);
+
+ return 0;
+ }
+
+ png_write_info(png_ptr, info_ptr);
+
+ vdata = data = mymalloc(samples_per_row * sizeof(unsigned));
+ vtran_data = tran_data = mymalloc(samples_per_row * 2);
+ for (y = 0; y < im->ysize; y++) {
+ i_img_dim i;
+ unsigned char *p = tran_data;
+ i_gsamp_bits(im, 0, im->xsize, y, data, NULL, im->channels, 16);
+ for (i = 0; i < samples_per_row; ++i) {
+ p[0] = data[i] >> 8;
+ p[1] = data[i] & 0xff;
+ p += 2;
+ }
+ png_write_row(png_ptr, (png_bytep)tran_data);
+ }
+ myfree(tran_data);
+ myfree(data);
+
+ return 1;
+}
+
+static int
+write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits) {
+ unsigned char *data, *volatile vdata = NULL;
+ i_img_dim y;
+ unsigned char pal_map[256];
+ png_color pcolors[256];
+ i_color colors[256];
+ int count = i_colorcount(im);
+ int i;
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ if (vdata)
+ myfree(vdata);
+
+ return 0;
+ }
+
+ i_getcolors(im, 0, colors, count);
+ if (im->channels < 3) {
+ /* convert the greyscale palette to color */
+ int i;
+ for (i = 0; i < count; ++i) {
+ i_color *c = colors + i;
+ c->channel[3] = c->channel[1];
+ c->channel[2] = c->channel[1] = c->channel[0];
+ }
+ }
+
+ if (i_img_has_alpha(im)) {
+ int i;
+ int bottom_index = 0, top_index = count-1;
+
+ /* fill out the palette map */
+ for (i = 0; i < count; ++i)
+ pal_map[i] = i;
+
+ /* the PNG spec suggests sorting the palette by alpha, but that's
+ unnecessary - all we want to do is move the opaque entries to
+ the end */
+ while (bottom_index < top_index) {
+ if (colors[bottom_index].rgba.a == 255) {
+ pal_map[bottom_index] = top_index;
+ pal_map[top_index--] = bottom_index;
+ }
+ ++bottom_index;
+ }
+ }
+
+ for (i = 0; i < count; ++i) {
+ int srci = i_img_has_alpha(im) ? pal_map[i] : i;
+
+ pcolors[i].red = colors[srci].rgb.r;
+ pcolors[i].green = colors[srci].rgb.g;
+ pcolors[i].blue = colors[srci].rgb.b;
+ }
+
+ png_set_PLTE(png_ptr, info_ptr, pcolors, count);
+
+ if (i_img_has_alpha(im)) {
+ unsigned char trans[256];
+ int i;
+
+ for (i = 0; i < count && colors[pal_map[i]].rgba.a != 255; ++i) {
+ trans[i] = colors[pal_map[i]].rgba.a;
+ }
+ png_set_tRNS(png_ptr, info_ptr, trans, i, NULL);
+ }
+
+ png_write_info(png_ptr, info_ptr);
+
+ png_set_packing(png_ptr);
+
+ vdata = data = mymalloc(im->xsize);
+ for (y = 0; y < im->ysize; y++) {
+ i_gpal(im, 0, im->xsize, y, data);
+ if (i_img_has_alpha(im)) {
+ i_img_dim x;
+ for (x = 0; x < im->xsize; ++x)
+ data[x] = pal_map[data[x]];
+ }
+ png_write_row(png_ptr, (png_bytep)data);
+ }
+ myfree(data);
+
+ return 1;
+}
+
+static int
+write_bilevel(png_structp png_ptr, png_infop info_ptr, i_img *im) {
+ unsigned char *data, *volatile vdata = NULL;
+ i_img_dim y;
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ if (vdata)
+ myfree(vdata);
+
+ return 0;
+ }
+
+ png_write_info(png_ptr, info_ptr);
+
+ png_set_packing(png_ptr);
+
+ vdata = data = mymalloc(im->xsize);
+ for (y = 0; y < im->ysize; y++) {
+ i_gsamp(im, 0, im->xsize, y, data, NULL, 1);
+ png_write_row(png_ptr, (png_bytep)data);
+ }
+ myfree(data);
+
+ return 1;
}
static void