X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=images%2Fio-libpng.c;h=e62e5ebae34a105fd25ced94506718edfb20a7d9;hb=0db6e10eac28f38bfc3b325b13ad95107c58ce1e;hp=6ed0516e82d5c7a8a214991ac4ef9eb41c43f04a;hpb=cf642e7a8a8db28fd343ddebc1a5b852beff6153;p=libucw.git diff --git a/images/io-libpng.c b/images/io-libpng.c index 6ed0516e..e62e5eba 100644 --- a/images/io-libpng.c +++ b/images/io-libpng.c @@ -9,14 +9,18 @@ #undef LOCAL_DEBUG -#include "lib/lib.h" -#include "lib/mempool.h" -#include "lib/fastbuf.h" -#include "images/images.h" +#include +#include +#include +#include +#include +#include +#include + #include #include -struct libpng_internals { +struct libpng_read_data { png_structp png_ptr; png_infop info_ptr; png_infop end_ptr; @@ -40,10 +44,18 @@ libpng_free(png_structp png_ptr UNUSED, png_voidp ptr UNUSED) } static void NONRET -libpng_error(png_structp png_ptr, png_const_charp msg) +libpng_read_error(png_structp png_ptr, png_const_charp msg) +{ + DBG("libpng_read_error()"); + IMAGE_ERROR(png_get_error_ptr(png_ptr), IMAGE_ERROR_READ_FAILED, "%s", msg); + longjmp(png_jmpbuf(png_ptr), 1); +} + +static void NONRET +libpng_write_error(png_structp png_ptr, png_const_charp msg) { - DBG("libpng_error()"); - image_thread_err(png_get_error_ptr(png_ptr), IMAGE_ERR_READ_FAILED, (byte *)msg); + DBG("libpng_write_error()"); + IMAGE_ERROR(png_get_error_ptr(png_ptr), IMAGE_ERROR_WRITE_FAILED, "%s", msg); longjmp(png_jmpbuf(png_ptr), 1); } @@ -54,88 +66,119 @@ libpng_warning(png_structp png_ptr UNUSED, png_const_charp msg UNUSED) } static void -libpng_read(png_structp png_ptr, png_bytep data, png_size_t length) +libpng_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) { - DBG("libpng_read(): len=%d", (uns)length); - if (unlikely(bread(png_get_io_ptr(png_ptr), data, length) < length)) + DBG("libpng_read_fn(len=%u)", (uns)length); + if (unlikely(bread((struct fastbuf *)png_get_io_ptr(png_ptr), (byte *)data, length) < length)) png_error(png_ptr, "Incomplete data"); } +static void +libpng_write_fn(png_structp png_ptr, png_bytep data, png_size_t length) +{ + DBG("libpng_write_fn(len=%u)", (uns)length); + bwrite((struct fastbuf *)png_get_io_ptr(png_ptr), (byte *)data, length); +} + +static void +libpng_flush_fn(png_structp png_ptr UNUSED) +{ + DBG("libpng_flush_fn()"); +} + static void libpng_read_cancel(struct image_io *io) { DBG("libpng_read_cancel()"); - struct libpng_internals *i = io->read_data; - png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr); + + struct libpng_read_data *rd = io->read_data; + png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr); } int libpng_read_header(struct image_io *io) { DBG("libpng_read_header()"); - struct libpng_internals *i = io->read_data = mp_alloc(io->internal_pool, sizeof(*i)); - i->png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, - io->thread, libpng_error, libpng_warning, + + /* Create libpng structures */ + struct libpng_read_data *rd = io->read_data = mp_alloc(io->internal_pool, sizeof(*rd)); + rd->png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, + io->context, libpng_read_error, libpng_warning, io->internal_pool, libpng_malloc, libpng_free); - if (unlikely(!i->png_ptr)) - goto err_create; - i->info_ptr = png_create_info_struct(i->png_ptr); - if (unlikely(!i->info_ptr)) + if (unlikely(!rd->png_ptr)) + { + IMAGE_ERROR(io->context, IMAGE_ERROR_READ_FAILED, "Cannot create libpng read structure."); + return 0; + } + rd->info_ptr = png_create_info_struct(rd->png_ptr); + if (unlikely(!rd->info_ptr)) { - png_destroy_read_struct(&i->png_ptr, NULL, NULL); - goto err_create; + IMAGE_ERROR(io->context, IMAGE_ERROR_READ_FAILED, "Cannot create libpng info structure."); + png_destroy_read_struct(&rd->png_ptr, NULL, NULL); + return 0; } - i->end_ptr = png_create_info_struct(i->png_ptr); - if (unlikely(!i->end_ptr)) + rd->end_ptr = png_create_info_struct(rd->png_ptr); + if (unlikely(!rd->end_ptr)) { - png_destroy_read_struct(&i->png_ptr, &i->info_ptr, NULL); - goto err_create; + IMAGE_ERROR(io->context, IMAGE_ERROR_READ_FAILED, "Cannot create libpng info structure."); + png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, NULL); + return 0; } - if (setjmp(png_jmpbuf(i->png_ptr))) + + /* Setup libpng longjump */ + if (unlikely(setjmp(png_jmpbuf(rd->png_ptr)))) { DBG("Libpng failed to read the image, longjump saved us"); - png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr); + png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr); return 0; } - png_set_read_fn(i->png_ptr, io->fastbuf, libpng_read); - png_set_user_limits(i->png_ptr, 0xffff, 0xffff); - - DBG("Reading image info"); - png_read_info(i->png_ptr, i->info_ptr); - png_get_IHDR(i->png_ptr, i->info_ptr, &i->cols, &i->rows, &i->bit_depth, &i->color_type, NULL, NULL, NULL); - - if (!io->cols) - io->cols = i->cols; - if (!io->rows) - io->rows = i->rows; - if (!(io->flags & IMAGE_CHANNELS_FORMAT)) - switch (i->color_type) - { - case PNG_COLOR_TYPE_GRAY: - io->flags |= COLOR_SPACE_GRAYSCALE; - break; - case PNG_COLOR_TYPE_GRAY_ALPHA: - io->flags |= COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA; - break; - case PNG_COLOR_TYPE_RGB: - io->flags |= COLOR_SPACE_RGB; - break; - case PNG_COLOR_TYPE_RGB_ALPHA: - case PNG_COLOR_TYPE_PALETTE: - io->flags |= COLOR_SPACE_RGB | IMAGE_ALPHA; - break; - default: - png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr); - image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Unknown color type"); - break; - } + /* Setup libpng IO */ + png_set_read_fn(rd->png_ptr, io->fastbuf, libpng_read_fn); + png_set_user_limits(rd->png_ptr, image_max_dim, image_max_dim); + + /* Read header */ + png_read_info(rd->png_ptr, rd->info_ptr); + png_get_IHDR(rd->png_ptr, rd->info_ptr, &rd->cols, &rd->rows, &rd->bit_depth, &rd->color_type, NULL, NULL, NULL); + + /* Fill image_io values */ + io->cols = rd->cols; + io->rows = rd->rows; + switch (rd->color_type) + { + case PNG_COLOR_TYPE_GRAY: + io->flags = COLOR_SPACE_GRAYSCALE; + io->number_of_colors = 1 << 8; + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + io->flags = COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA; + io->number_of_colors = 1 << 8; + break; + case PNG_COLOR_TYPE_RGB: + io->flags = COLOR_SPACE_RGB; + io->number_of_colors = 1 << 24; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + io->number_of_colors = 1 << 24; + io->flags = COLOR_SPACE_RGB | IMAGE_ALPHA; + break; + case PNG_COLOR_TYPE_PALETTE: + io->flags = COLOR_SPACE_RGB | IMAGE_ALPHA | IMAGE_IO_HAS_PALETTE; + int num_palette; + if (png_get_PLTE(rd->png_ptr, rd->info_ptr, NULL, &num_palette)) + io->number_of_colors = num_palette; + else + io->number_of_colors = 1 << rd->bit_depth; + break; + default: + png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr); + IMAGE_ERROR(io->context, IMAGE_ERROR_READ_FAILED, "Unknown color type"); + break; + } + + /* Success */ io->read_cancel = libpng_read_cancel; return 1; - -err_create: - image_thread_err(io->thread, IMAGE_ERR_READ_FAILED, "Cannot create libpng read structure."); - return 0; } int @@ -143,126 +186,197 @@ libpng_read_data(struct image_io *io) { DBG("libpng_read_data()"); - struct libpng_internals *i = io->read_data; + struct libpng_read_data *rd = io->read_data; - /* Test supported pixel formats */ - switch (io->flags & IMAGE_COLOR_SPACE) - { - case COLOR_SPACE_GRAYSCALE: - case COLOR_SPACE_RGB: - break; - default: - png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr); - image_thread_err(io->thread, IMAGE_ERR_INVALID_PIXEL_FORMAT, "Unsupported color space."); - return 0; - } + struct image_io_read_data_internals rdi; + rdi.image = NULL; - volatile int need_scale = io->cols != i->cols || io->rows != i->rows; - struct image * volatile img = need_scale ? - image_new(io->thread, i->cols, i->rows, io->flags & IMAGE_PIXEL_FORMAT, NULL) : - image_new(io->thread, i->cols, i->rows, io->flags, io->pool); - if (!img) - { - png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr); - return 0; - } - - if (setjmp(png_jmpbuf(i->png_ptr))) + if (setjmp(png_jmpbuf(rd->png_ptr))) { DBG("Libpng failed to read the image, longjump saved us"); - png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr); - if (need_scale || !io->pool) - image_destroy(io->thread, img); + png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr); + if (rdi.image) + image_io_read_data_break(&rdi, io); return 0; } + uns read_flags = io->flags; + /* Apply transformations */ - if (i->bit_depth == 16) - png_set_strip_16(i->png_ptr); - switch (i->color_type) + if (rd->bit_depth == 16) + png_set_strip_16(rd->png_ptr); + switch (rd->color_type) { case PNG_COLOR_TYPE_PALETTE: - if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE) + if ((read_flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE) { - png_set_palette_to_rgb(i->png_ptr); - png_set_rgb_to_gray_fixed(i->png_ptr, 1, 21267, 71514); + png_set_palette_to_rgb(rd->png_ptr); + png_set_rgb_to_gray_fixed(rd->png_ptr, 1, 21267, 71514); } else - png_set_palette_to_rgb(i->png_ptr); - if ((io->flags & IMAGE_ALPHA) || (io->flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED)) - png_set_add_alpha(i->png_ptr, 255, PNG_FILLER_AFTER); + { + png_set_palette_to_rgb(rd->png_ptr); + read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_RGB; + } + if (!(read_flags & IMAGE_ALPHA)) + { + if (io->flags & IMAGE_IO_USE_BACKGROUND) + { + png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER); + read_flags = (read_flags & IMAGE_CHANNELS_FORMAT) | IMAGE_ALPHA; + } + else if ((read_flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED)) + png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER); + else + png_set_strip_alpha(rd->png_ptr); + } else - png_set_strip_alpha(i->png_ptr); + png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER); break; case PNG_COLOR_TYPE_GRAY: - if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_RGB) - png_set_gray_to_rgb(i->png_ptr); - if (io->flags & IMAGE_ALPHA) - png_set_add_alpha(i->png_ptr, 255, PNG_FILLER_AFTER); + if ((read_flags & IMAGE_COLOR_SPACE) != COLOR_SPACE_GRAYSCALE) + { + png_set_gray_to_rgb(rd->png_ptr); + read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_RGB; + } + if (read_flags & IMAGE_ALPHA) + png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER); break; case PNG_COLOR_TYPE_GRAY_ALPHA: - if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_RGB) - png_set_gray_to_rgb(i->png_ptr); - if (!(io->flags & IMAGE_ALPHA)) - png_set_strip_alpha(i->png_ptr); + if ((read_flags & IMAGE_COLOR_SPACE) != COLOR_SPACE_GRAYSCALE) + { + png_set_gray_to_rgb(rd->png_ptr); + read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_RGB; + } + if (!(read_flags & IMAGE_ALPHA)) + { + if (io->flags & IMAGE_IO_USE_BACKGROUND) + read_flags = (read_flags & IMAGE_CHANNELS_FORMAT) | IMAGE_ALPHA; + else + png_set_strip_alpha(rd->png_ptr); + } break; case PNG_COLOR_TYPE_RGB: - if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE) - png_set_rgb_to_gray_fixed(i->png_ptr, 1, 21267, 71514); - if ((io->flags & IMAGE_ALPHA) || (io->flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED)) - png_set_add_alpha(i->png_ptr, 255, PNG_FILLER_AFTER); + if ((read_flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE) + png_set_rgb_to_gray_fixed(rd->png_ptr, 1, 21267, 71514); + else + read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_RGB; + if ((read_flags & IMAGE_ALPHA) || (read_flags & IMAGE_PIXEL_FORMAT) == (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED)) + png_set_add_alpha(rd->png_ptr, 255, PNG_FILLER_AFTER); break; case PNG_COLOR_TYPE_RGB_ALPHA: - if ((io->flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE) - png_set_rgb_to_gray_fixed(i->png_ptr, 1, 21267, 71514); - if (!(io->flags & IMAGE_ALPHA) && (io->flags & IMAGE_PIXEL_FORMAT) != (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED)) - png_set_strip_alpha(i->png_ptr); + if ((read_flags & IMAGE_COLOR_SPACE) == COLOR_SPACE_GRAYSCALE) + png_set_rgb_to_gray_fixed(rd->png_ptr, 1, 21267, 71514); + else + read_flags = (read_flags & ~IMAGE_COLOR_SPACE & IMAGE_CHANNELS_FORMAT) | COLOR_SPACE_RGB; + if (!(read_flags & IMAGE_ALPHA)) + if (io->flags & IMAGE_IO_USE_BACKGROUND) + read_flags = (read_flags & IMAGE_CHANNELS_FORMAT) | IMAGE_ALPHA; + else if ((read_flags & IMAGE_PIXEL_FORMAT) != (COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED)) + png_set_strip_alpha(rd->png_ptr); break; default: ASSERT(0); } - png_read_update_info(i->png_ptr, i->info_ptr); + png_read_update_info(rd->png_ptr, rd->info_ptr); + /* Prepare the image */ + if (unlikely(!image_io_read_data_prepare(&rdi, io, rd->cols, rd->rows, read_flags))) + { + png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr); + return 0; + } /* Read image data */ DBG("Reading image data"); + struct image *img = rdi.image; byte *pixels = img->pixels; png_bytep rows[img->rows]; for (uns r = 0; r < img->rows; r++, pixels += img->row_size) rows[r] = (png_bytep)pixels; - png_read_image(i->png_ptr, rows); - png_read_end(i->png_ptr, i->end_ptr); + png_read_image(rd->png_ptr, rows); + png_read_end(rd->png_ptr, rd->end_ptr); /* Destroy libpng read structure */ - png_destroy_read_struct(&i->png_ptr, &i->info_ptr, &i->end_ptr); + png_destroy_read_struct(&rd->png_ptr, &rd->info_ptr, &rd->end_ptr); - /* Scale and store the resulting image */ - if (need_scale) - { - struct image *dest = image_new(io->thread, io->cols, io->rows, io->flags, io->pool); - if (!dest) - { - image_destroy(io->thread, img); - return 0; - } - if (!image_scale(io->thread, dest, img)) - { - image_destroy(io->thread, img); - if (!io->pool) - image_destroy(io->thread, dest); - return 0; - } - io->image = dest; - } - else - io->image = img; - io->image_destroy = !io->pool; - - return 1; + /* Finish the image */ + return image_io_read_data_finish(&rdi, io); } int libpng_write(struct image_io *io) { - image_thread_err(io->thread, IMAGE_ERR_NOT_IMPLEMENTED, "Libpng writing not implemented."); - return 0; + DBG("libpng_write()"); + + /* Create libpng structures */ + png_structp png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, + io->context, libpng_write_error, libpng_warning, + io->internal_pool, libpng_malloc, libpng_free); + if (unlikely(!png_ptr)) + { + IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "Cannot create libpng write structure."); + return 0; + } + png_infop info_ptr = png_create_info_struct(png_ptr); + if (unlikely(!info_ptr)) + { + IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "Cannot create libpng info structure."); + png_destroy_write_struct(&png_ptr, NULL); + return 0; + } + + /* Setup libpng longjump */ + if (unlikely(setjmp(png_jmpbuf(png_ptr)))) + { + DBG("Libpng failed to write the image, longjump saved us."); + png_destroy_write_struct(&png_ptr, &info_ptr); + return 0; + } + + /* Setup libpng IO */ + png_set_write_fn(png_ptr, io->fastbuf, libpng_write_fn, libpng_flush_fn); + + /* Setup PNG parameters */ + struct image *img = io->image; + switch (img->flags & IMAGE_PIXEL_FORMAT) + { + case COLOR_SPACE_GRAYSCALE | IMAGE_PIXELS_ALIGNED: + png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_GRAY, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + break; + case COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA | IMAGE_PIXELS_ALIGNED: + png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_GRAY_ALPHA, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + break; + case COLOR_SPACE_RGB: + png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + break; + case COLOR_SPACE_RGB | IMAGE_ALPHA | IMAGE_PIXELS_ALIGNED: + png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_RGB_ALPHA, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + break; + case COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED: + png_set_IHDR(png_ptr, info_ptr, img->cols, img->rows, 8, PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); + break; + default: + IMAGE_ERROR(io->context, IMAGE_ERROR_WRITE_FAILED, "Libpng does not support this pixel format (0x%x)", img->flags & IMAGE_PIXEL_FORMAT); + png_destroy_write_struct(&png_ptr, &info_ptr); + return 0; + } + png_write_info(png_ptr, info_ptr); + + /* Write pixels */ + byte *pixels = img->pixels; + png_bytep rows[img->rows]; + for (uns r = 0; r < img->rows; r++, pixels += img->row_size) + rows[r] = (png_bytep)pixels; + png_write_image(png_ptr, rows); + png_write_end(png_ptr, info_ptr); + + /* Free libpng structure */ + png_destroy_write_struct(&png_ptr, &info_ptr); + return 1; }