diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2008-01-18 14:53:50 +0000 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2008-03-04 09:31:21 +0000 |
commit | 11a2444ec875aaaed12c1f1cfed5eb8e139c306d (patch) | |
tree | e0e751f3b4654da1b823a99cacfc1cfc63f9739c | |
parent | 06b375aee999220ce294c22fa50a3040c19d5492 (diff) |
[cairo-png] Support generating CAIRO_FORMAT_RGB24 from PNGs.
If the PNG does not have an alpha channel, then create an opaque image.
-rw-r--r-- | src/cairo-png.c | 69 | ||||
-rw-r--r-- | test/.gitignore | 1 | ||||
-rw-r--r-- | test/Makefile.am | 2 | ||||
-rw-r--r-- | test/png.c | 149 |
4 files changed, 204 insertions, 17 deletions
diff --git a/src/cairo-png.c b/src/cairo-png.c index de09086ab..bdfeda23e 100644 --- a/src/cairo-png.c +++ b/src/cairo-png.c @@ -33,6 +33,7 @@ * Contributor(s): * Carl D. Worth <cworth@cworth.org> * Kristian Høgsberg <krh@redhat.com> + * Chris Wilson <chris@chris-wilson.co.uk> */ #include "cairoint.h" @@ -304,7 +305,7 @@ cairo_surface_write_to_png (cairo_surface_t *surface, } struct png_write_closure_t { - cairo_write_func_t write_func; + cairo_write_func_t write_func; void *closure; }; @@ -368,21 +369,21 @@ premultiply_data (png_structp png, unsigned int i; for (i = 0; i < row_info->rowbytes; i += 4) { - uint8_t *base = &data[i]; + uint8_t *base = &data[i]; uint8_t alpha = base[3]; uint32_t p; if (alpha == 0) { p = 0; } else { - uint8_t red = base[0]; + uint8_t red = base[0]; uint8_t green = base[1]; - uint8_t blue = base[2]; + uint8_t blue = base[2]; if (alpha != 0xff) { - red = multiply_alpha (alpha, red); + red = multiply_alpha (alpha, red); green = multiply_alpha (alpha, green); - blue = multiply_alpha (alpha, blue); + blue = multiply_alpha (alpha, blue); } p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0); } @@ -390,6 +391,24 @@ premultiply_data (png_structp png, } } +/* Converts RGBx bytes to native endian xRGB */ +static void +convert_bytes_to_data (png_structp png, png_row_infop row_info, png_bytep data) +{ + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) { + uint8_t *base = &data[i]; + uint8_t red = base[0]; + uint8_t green = base[1]; + uint8_t blue = base[2]; + uint32_t pixel; + + pixel = (0xff << 24) | (red << 16) | (green << 8) | (blue << 0); + memcpy (base, &pixel, sizeof (uint32_t)); + } +} + static cairo_surface_t * read_png (png_rw_ptr read_func, void *closure) @@ -402,6 +421,7 @@ read_png (png_rw_ptr read_func, png_uint_32 png_width, png_height; int depth, color_type, interlace, stride; unsigned int i; + cairo_format_t format; cairo_status_t status; /* XXX: Perhaps we'll want some other error handlers? */ @@ -445,7 +465,7 @@ read_png (png_rw_ptr read_func, png_set_palette_to_rgb (png); /* expand gray bit depth if needed */ - if (color_type == PNG_COLOR_TYPE_GRAY && depth < 8) + if (color_type == PNG_COLOR_TYPE_GRAY && (depth > 1 && depth < 8)) #if PNG_LIBPNG_VER >= 10209 png_set_expand_gray_1_2_4_to_8 (png); #else @@ -462,20 +482,35 @@ read_png (png_rw_ptr read_func, png_set_packing (png); /* convert grayscale to RGB */ - if (color_type == PNG_COLOR_TYPE_GRAY - || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - png_set_gray_to_rgb (png); + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + png_set_gray_to_rgb (png); + } if (interlace != PNG_INTERLACE_NONE) png_set_interlace_handling (png); - png_set_filler (png, 0xff, PNG_FILLER_AFTER); + switch (color_type) { + default: + case PNG_COLOR_TYPE_GRAY_ALPHA: + case PNG_COLOR_TYPE_RGB_ALPHA: + format = CAIRO_FORMAT_ARGB32; + png_set_read_user_transform_fn (png, premultiply_data); + break; - png_set_read_user_transform_fn (png, premultiply_data); + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_PALETTE: + case PNG_COLOR_TYPE_RGB: + format = CAIRO_FORMAT_RGB24; + png_set_read_user_transform_fn (png, convert_bytes_to_data); + png_set_filler (png, 0xff, PNG_FILLER_AFTER); + break; + } png_read_update_info (png, info); - stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, png_width); + stride = cairo_format_stride_for_width (format, png_width); if (stride < 0) { surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); goto BAIL; @@ -504,9 +539,9 @@ read_png (png_rw_ptr read_func, goto BAIL; } - surface = cairo_image_surface_create_for_data (data, - CAIRO_FORMAT_ARGB32, - png_width, png_height, stride); + surface = cairo_image_surface_create_for_data (data, format, + png_width, png_height, + stride); if (surface->status) goto BAIL; @@ -590,7 +625,7 @@ cairo_image_surface_create_from_png (const char *filename) } struct png_read_closure_t { - cairo_read_func_t read_func; + cairo_read_func_t read_func; void *closure; }; diff --git a/test/.gitignore b/test/.gitignore index e56151d5b..07475218e 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -123,6 +123,7 @@ pdf-features pdf-features.pdf pdf-surface-source pdf-surface-source.pdf +png png-flatten ps-features ps-features.ps diff --git a/test/Makefile.am b/test/Makefile.am index 30207119e..7201a98c8 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -96,6 +96,7 @@ paint-with-alpha$(EXEEXT) \ pattern-get-type$(EXEEXT) \ pattern-getters$(EXEEXT) \ pixman-rotate$(EXEEXT) \ +png$(EXEEXT) \ push-group$(EXEEXT) \ radial-gradient$(EXEEXT) \ random-intersections$(EXEEXT) \ @@ -623,6 +624,7 @@ fallback-resolution \ font-options \ multi-page \ pdf-features \ +png \ ps-features \ svg-clip \ svg-surface \ diff --git a/test/png.c b/test/png.c new file mode 100644 index 000000000..65e465dfd --- /dev/null +++ b/test/png.c @@ -0,0 +1,149 @@ +/* + * Copyright © 2008 Chris Wilson + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Chris Wilson not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. Chris Wilson makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Chris Wilson <chris@chris-wilson.co.uk> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cairo-test.h" + +#include <cairo.h> +#include <stdio.h> +#include <stdint.h> +#include <assert.h> + +/* Test the idempotency of write_png->read_png */ + +#define RGB_MASK 0x00ffffff + +static cairo_bool_t +image_surface_equals (cairo_surface_t *A, cairo_surface_t *B) +{ + if (cairo_image_surface_get_format (A) != + cairo_image_surface_get_format (B)) + return 0; + + if (cairo_image_surface_get_width (A) != + cairo_image_surface_get_width (B)) + return 0; + + if (cairo_image_surface_get_height (A) != + cairo_image_surface_get_height (B)) + return 0; + + return 1; +} + +static const char * +format_to_string (cairo_format_t format) +{ + switch (format) { + case CAIRO_FORMAT_A1: return "a1"; + case CAIRO_FORMAT_A8: return "a8"; + case CAIRO_FORMAT_RGB24: return "rgb24"; + case CAIRO_FORMAT_ARGB32: return "argb32"; + default: return "???"; + } +} + +static void +print_surface (cairo_surface_t *surface) +{ + printf ("%s (%dx%d)\n", + format_to_string (cairo_image_surface_get_format (surface)), + cairo_image_surface_get_width (surface), + cairo_image_surface_get_height (surface)); +} + +int +main (void) +{ + cairo_surface_t *surface0, *surface1; + cairo_status_t status; + uint32_t argb32 = 0xdeadbede; + + surface0 = cairo_image_surface_create_for_data ((unsigned char *) &argb32, + CAIRO_FORMAT_ARGB32, + 1, 1, 4); + assert (cairo_surface_status (surface0) == CAIRO_STATUS_SUCCESS); + status = cairo_surface_write_to_png (surface0, "png-test.png"); + if (status) { + printf ("Error writing 'png-test.png': %s\n", + cairo_status_to_string (status)); + return CAIRO_TEST_FAILURE; + } + surface1 = cairo_image_surface_create_from_png ("png-test.png"); + status = cairo_surface_status (surface1); + if (status) { + printf ("Error reading 'png-test.png': %s\n", + cairo_status_to_string (status)); + return CAIRO_TEST_FAILURE; + } + + if (! image_surface_equals (surface0, surface1)) { + printf ("Error surface mismatch.\n"); + printf ("to png: "); print_surface (surface0); + printf ("from png: "); print_surface (surface1); + return CAIRO_TEST_FAILURE; + } + assert (*(uint32_t *) cairo_image_surface_get_data (surface1) == argb32); + + cairo_surface_destroy (surface0); + cairo_surface_destroy (surface1); + + + surface0 = cairo_image_surface_create_for_data ((unsigned char *) &argb32, + CAIRO_FORMAT_RGB24, + 1, 1, 4); + assert (cairo_surface_status (surface0) == CAIRO_STATUS_SUCCESS); + status = cairo_surface_write_to_png (surface0, "png-test.png"); + if (status) { + printf ("Error writing 'png-test.png': %s\n", + cairo_status_to_string (status)); + return CAIRO_TEST_FAILURE; + } + surface1 = cairo_image_surface_create_from_png ("png-test.png"); + status = cairo_surface_status (surface1); + if (status) { + printf ("Error reading 'png-test.png': %s\n", + cairo_status_to_string (status)); + return CAIRO_TEST_FAILURE; + } + + if (! image_surface_equals (surface0, surface1)) { + printf ("Error surface mismatch.\n"); + printf ("to png: "); print_surface (surface0); + printf ("from png: "); print_surface (surface1); + return CAIRO_TEST_FAILURE; + } + assert ((*(uint32_t *) cairo_image_surface_get_data (surface1) & RGB_MASK) + == (argb32 & RGB_MASK)); + + cairo_surface_destroy (surface0); + cairo_surface_destroy (surface1); + + + return CAIRO_TEST_SUCCESS; +} |