summaryrefslogtreecommitdiff
path: root/src/cairo_png_surface.c
diff options
context:
space:
mode:
authorCarl Worth <cworth@cworth.org>2004-05-04 09:25:54 +0000
committerCarl Worth <cworth@cworth.org>2004-05-04 09:25:54 +0000
commit7398aa8e1e4328a85f8d2de0c5180712cfa842e8 (patch)
treee60c33b58ad5fc87555f0f012b3e53b9f26065f2 /src/cairo_png_surface.c
parent423ea55410d86c8c8cc4fb5219941bcbdeab2a33 (diff)
Move all libpng-related code into copy_page. (unpremultiply_data): Add missing unpremultiply step. (_cairo_png_surface_copy_page): Move PNG output to copy_page. Add support for A8 and A1 images. Remove time from header (we may want to add it again later, but for now it messes up my test suite). (_cairo_png_surface_destroy): Call copy_page if it hasn't been called already. (_cairo_png_surface_show_page): Add implementation.
Diffstat (limited to 'src/cairo_png_surface.c')
-rw-r--r--src/cairo_png_surface.c232
1 files changed, 137 insertions, 95 deletions
diff --git a/src/cairo_png_surface.c b/src/cairo_png_surface.c
index 851df98d..0f5552aa 100644
--- a/src/cairo_png_surface.c
+++ b/src/cairo_png_surface.c
@@ -4,6 +4,9 @@
static const cairo_surface_backend_t cairo_png_surface_backend;
+static cairo_int_status_t
+_cairo_png_surface_copy_page (void *abstract_surface);
+
void
cairo_set_target_png (cairo_t *cr,
FILE *file,
@@ -31,12 +34,12 @@ typedef struct cairo_png_surface {
cairo_surface_t base;
/* PNG-specific fields */
+ cairo_image_surface_t *image;
FILE *file;
+ int copied;
- png_structp png_w;
- png_infop png_i;
+ cairo_format_t format;
- cairo_image_surface_t *image;
} cairo_png_surface_t;
@@ -50,98 +53,29 @@ cairo_png_surface_create (FILE *file,
int height)
{
cairo_png_surface_t *surface;
- time_t now = time (NULL);
- png_time png_time;
-
- if (format == CAIRO_FORMAT_A8 ||
- format == CAIRO_FORMAT_A1 ||
- file == NULL)
- return NULL;
surface = malloc (sizeof (cairo_png_surface_t));
if (surface == NULL)
- goto failure;
+ return NULL;
_cairo_surface_init (&surface->base, &cairo_png_surface_backend);
- surface->png_w = NULL;
- surface->png_i = NULL;
surface->image = (cairo_image_surface_t *)
cairo_image_surface_create (format, width, height);
- if (surface->image == NULL)
- goto failure;
-
- _cairo_png_surface_erase (surface);
-
- surface->file = file;
- surface->png_w = png_create_write_struct (PNG_LIBPNG_VER_STRING,
- NULL, NULL, NULL);
- if (surface->png_w == NULL)
- goto failure;
- surface->png_i = png_create_info_struct (surface->png_w);
- if (surface->png_i == NULL)
- goto failure;
-
- if (setjmp (png_jmpbuf (surface->png_w)))
- goto failure;
-
- png_init_io (surface->png_w, surface->file);
-
- switch (format) {
- case CAIRO_FORMAT_ARGB32:
- png_set_IHDR (surface->png_w, surface->png_i,
- width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA,
- PNG_INTERLACE_NONE,
- PNG_COMPRESSION_TYPE_DEFAULT,
- PNG_FILTER_TYPE_DEFAULT);
- break;
- case CAIRO_FORMAT_RGB24:
- png_set_IHDR (surface->png_w, surface->png_i,
- width, height, 8, PNG_COLOR_TYPE_RGB,
- PNG_INTERLACE_NONE,
- PNG_COMPRESSION_TYPE_DEFAULT,
- PNG_FILTER_TYPE_DEFAULT);
- break;
- case CAIRO_FORMAT_A8:
- case CAIRO_FORMAT_A1:
- /* These are not currently supported. */
- break;
+ if (surface->image == NULL) {
+ free (surface);
+ return NULL;
}
- png_convert_from_time_t (&png_time, now);
- png_set_tIME (surface->png_w, surface->png_i, &png_time);
+ _cairo_png_surface_erase (surface);
- png_write_info (surface->png_w, surface->png_i);
+ surface->file = file;
+ surface->copied = 0;
- switch (format) {
- case CAIRO_FORMAT_ARGB32:
- png_set_bgr (surface->png_w);
- break;
- case CAIRO_FORMAT_RGB24:
- png_set_filler (surface->png_w, 0, PNG_FILLER_AFTER);
- png_set_bgr (surface->png_w);
- break;
- case CAIRO_FORMAT_A8:
- case CAIRO_FORMAT_A1:
- /* These are not currently supported. */
- break;
- }
+ surface->format = format;
return &surface->base;
-
-
- failure:
- if (surface) {
- if (surface->image)
- cairo_surface_destroy (&surface->image->base);
- if (surface->png_i)
- png_destroy_write_struct (&surface->png_w, &surface->png_i);
- else if (surface->png_w)
- png_destroy_write_struct (&surface->png_w, NULL);
- free (surface);
- }
- return NULL;
}
@@ -155,25 +89,35 @@ _cairo_png_surface_create_similar (void *abstract_src,
}
static void
-_cairo_png_surface_destroy (void *abstract_surface)
+unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data)
{
- cairo_png_surface_t *surface = abstract_surface;
int i;
- png_byte *row;
-
- if (setjmp (png_jmpbuf (surface->png_w)))
- goto failure;
- row = surface->image->data;
- for (i=0; i < surface->image->height; i++) {
- png_write_row (surface->png_w, row);
- row += surface->image->stride;
+ for (i = 0; i < row_info->rowbytes; i += 4) {
+ unsigned char *b = &data[i];
+ unsigned int pixel;
+ unsigned char alpha;
+
+ memcpy (&pixel, b, sizeof (unsigned int));
+ alpha = (pixel & 0xff000000) >> 24;
+ if (alpha == 0) {
+ b[0] = b[1] = b[2] = b[3] = 0;
+ } else {
+ b[0] = (((pixel & 0x0000ff) >> 0) * 255) / alpha;
+ b[1] = (((pixel & 0x00ff00) >> 8) * 255) / alpha;
+ b[2] = (((pixel & 0xff0000) >> 16) * 255) / alpha;
+ b[3] = alpha;
+ }
}
+}
- png_write_end (surface->png_w, surface->png_i);
+static void
+_cairo_png_surface_destroy (void *abstract_surface)
+{
+ cairo_png_surface_t *surface = abstract_surface;
- failure:
- png_destroy_write_struct (&surface->png_w, &surface->png_i);
+ if (! surface->copied)
+ _cairo_png_surface_copy_page (surface);
cairo_surface_destroy (&surface->image->base);
@@ -296,13 +240,111 @@ _cairo_png_surface_composite_trapezoids (cairo_operator_t operator,
static cairo_int_status_t
_cairo_png_surface_copy_page (void *abstract_surface)
{
- return CAIRO_INT_STATUS_UNSUPPORTED;
+ int i;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_png_surface_t *surface = abstract_surface;
+ png_struct *png;
+ png_info *info;
+ png_byte **rows;
+ png_color_16 white;
+ int png_color_type;
+ int depth;
+
+ rows = malloc (surface->image->height * sizeof(png_byte*));
+ if (rows == NULL)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ for (i = 0; i < surface->image->height; i++)
+ rows[i] = surface->image->data + i * surface->image->stride;
+
+ png = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (png == NULL)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ info = png_create_info_struct (png);
+ if (info == NULL) {
+ png_destroy_write_struct (&png, NULL);
+ return CAIRO_STATUS_NO_MEMORY;
+ }
+
+ if (setjmp (png_jmpbuf (png))) {
+ status = CAIRO_STATUS_NO_MEMORY;
+ goto BAIL;
+ }
+
+ png_init_io (png, surface->file);
+
+ switch (surface->format) {
+ case CAIRO_FORMAT_ARGB32:
+ depth = 8;
+ png_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ break;
+ case CAIRO_FORMAT_RGB24:
+ depth = 8;
+ png_color_type = PNG_COLOR_TYPE_RGB;
+ break;
+ case CAIRO_FORMAT_A8:
+ depth = 8;
+ png_color_type = PNG_COLOR_TYPE_GRAY;
+ break;
+ case CAIRO_FORMAT_A1:
+ depth = 1;
+ png_color_type = PNG_COLOR_TYPE_GRAY;
+ break;
+ }
+
+ png_set_IHDR (png, info,
+ surface->image->width,
+ surface->image->height, depth,
+ png_color_type,
+ PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+
+ white.red = 0xff;
+ white.blue = 0xff;
+ white.green = 0xff;
+ png_set_bKGD (png, info, &white);
+
+/* XXX: Setting the time is interfereing with the image comparison
+ png_convert_from_time_t (&png_time, time (NULL));
+ png_set_tIME (png, info, &png_time);
+*/
+
+ png_set_write_user_transform_fn (png, unpremultiply_data);
+ if (surface->format == CAIRO_FORMAT_ARGB32 || surface->format == CAIRO_FORMAT_RGB24)
+ png_set_bgr (png);
+ if (surface->format == CAIRO_FORMAT_RGB24)
+ png_set_filler (png, 0, PNG_FILLER_AFTER);
+
+ png_write_info (png, info);
+ png_write_image (png, rows);
+ png_write_end (png, info);
+
+ surface->copied = 1;
+
+BAIL:
+ png_destroy_write_struct (&png, &info);
+
+ free (rows);
+ fclose (surface->file);
+
+ return status;
}
static cairo_int_status_t
_cairo_png_surface_show_page (void *abstract_surface)
{
- return CAIRO_INT_STATUS_UNSUPPORTED;
+ cairo_int_status_t status;
+ cairo_png_surface_t *surface = abstract_surface;
+
+ status = _cairo_png_surface_copy_page (surface);
+ if (status)
+ return status;
+
+ _cairo_png_surface_erase (surface);
+
+ return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t