[cairo-png] Support generating CAIRO_FORMAT_RGB24 from PNGs.

If the PNG does not have an alpha channel, then create an opaque image.
This commit is contained in:
Chris Wilson 2008-01-18 14:53:50 +00:00
parent 06b375aee9
commit 11a2444ec8
4 changed files with 204 additions and 17 deletions

View file

@ -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;
};

1
test/.gitignore vendored
View file

@ -123,6 +123,7 @@ pdf-features
pdf-features.pdf
pdf-surface-source
pdf-surface-source.pdf
png
png-flatten
ps-features
ps-features.ps

View file

@ -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 \

149
test/png.c Normal file
View file

@ -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;
}