diff options
Diffstat (limited to 'src/cairo_png_surface.c')
-rw-r--r-- | src/cairo_png_surface.c | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/src/cairo_png_surface.c b/src/cairo_png_surface.c new file mode 100644 index 00000000..ed7bb039 --- /dev/null +++ b/src/cairo_png_surface.c @@ -0,0 +1,333 @@ +#include <png.h> + +#include "cairoint.h" + +static const cairo_surface_backend_t cairo_png_surface_backend; + +void +cairo_set_target_png (cairo_t *cr, + FILE *file, + cairo_format_t format, + int width, + int height) +{ + cairo_surface_t *surface; + + surface = cairo_png_surface_create (file, format, + width, height); + + if (surface == NULL) { + cr->status = CAIRO_STATUS_NO_MEMORY; + return; + } + + cairo_set_target_surface (cr, surface); + + /* cairo_set_target_surface takes a reference, so we must destroy ours */ + cairo_surface_destroy (surface); +} + +typedef struct cairo_png_surface { + cairo_surface_t base; + + /* PNG-specific fields */ + FILE *file; + + png_structp png_w; + png_infop png_i; + + cairo_image_surface_t *image; +} cairo_png_surface_t; + + +static void +_cairo_png_surface_erase (cairo_png_surface_t *surface); + +cairo_surface_t * +cairo_png_surface_create (FILE *file, + cairo_format_t format, + int width, + 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; + + _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; + } + + png_convert_from_time_t (&png_time, now); + png_set_tIME (surface->png_w, surface->png_i, &png_time); + + png_write_info (surface->png_w, surface->png_i); + + 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; + } + + 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; +} + + +static cairo_surface_t * +_cairo_png_surface_create_similar (void *abstract_src, + cairo_format_t format, + int width, + int height) +{ + return NULL; +} + +static void +_cairo_png_surface_destroy (void *abstract_surface) +{ + 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; + } + + png_write_end (surface->png_w, surface->png_i); + + failure: + png_destroy_write_struct (&surface->png_w, &surface->png_i); + + cairo_surface_destroy (&surface->image->base); + + free (surface); +} + +static void +_cairo_png_surface_erase (cairo_png_surface_t *surface) +{ + cairo_color_t transparent; + + _cairo_color_init (&transparent); + _cairo_color_set_rgb (&transparent, 0., 0., 0.); + _cairo_color_set_alpha (&transparent, 0.); + _cairo_surface_fill_rectangle (&surface->image->base, + CAIRO_OPERATOR_SRC, + &transparent, + 0, 0, + surface->image->width, + surface->image->height); +} + +static double +_cairo_png_surface_pixels_per_inch (void *abstract_surface) +{ + return 96.0; +} + +static cairo_image_surface_t * +_cairo_png_surface_get_image (void *abstract_surface) +{ + cairo_png_surface_t *surface = abstract_surface; + + cairo_surface_reference (&surface->image->base); + + return surface->image; +} + +static cairo_status_t +_cairo_png_surface_set_image (void *abstract_surface, + cairo_image_surface_t *image) +{ + cairo_png_surface_t *surface = abstract_surface; + + if (image == surface->image) + return CAIRO_STATUS_SUCCESS; + + /* XXX: Need to call _cairo_image_surface_set_image here, but it's + not implemented yet. */ + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_status_t +_cairo_png_surface_set_matrix (void *abstract_surface, + cairo_matrix_t *matrix) +{ + cairo_png_surface_t *surface = abstract_surface; + + return _cairo_image_surface_set_matrix (surface->image, matrix); +} + +static cairo_status_t +_cairo_png_surface_set_filter (void *abstract_surface, + cairo_filter_t filter) +{ + cairo_png_surface_t *surface = abstract_surface; + + return _cairo_image_surface_set_filter (surface->image, filter); +} + +static cairo_status_t +_cairo_png_surface_set_repeat (void *abstract_surface, + int repeat) +{ + cairo_png_surface_t *surface = abstract_surface; + + return _cairo_image_surface_set_repeat (surface->image, repeat); +} + +static cairo_int_status_t +_cairo_png_surface_composite (cairo_operator_t operator, + cairo_surface_t *generic_src, + cairo_surface_t *generic_mask, + void *abstract_dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_png_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t operator, + const cairo_color_t *color, + cairo_rectangle_t *rects, + int num_rects) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_png_surface_composite_trapezoids (cairo_operator_t operator, + cairo_surface_t *generic_src, + void *abstract_dst, + int x_src, + int y_src, + cairo_trapezoid_t *traps, + int num_traps) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_png_surface_copy_page (void *abstract_surface) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_png_surface_show_page (void *abstract_surface) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_png_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) +{ + cairo_png_surface_t *surface = abstract_surface; + + return _cairo_image_surface_set_clip_region (surface->image, region); +} + +static cairo_int_status_t +_cairo_png_surface_create_pattern (void *abstract_surface, + cairo_pattern_t *pattern, + cairo_box_t *extents) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static const cairo_surface_backend_t cairo_png_surface_backend = { + _cairo_png_surface_create_similar, + _cairo_png_surface_destroy, + _cairo_png_surface_pixels_per_inch, + _cairo_png_surface_get_image, + _cairo_png_surface_set_image, + _cairo_png_surface_set_matrix, + _cairo_png_surface_set_filter, + _cairo_png_surface_set_repeat, + _cairo_png_surface_composite, + _cairo_png_surface_fill_rectangles, + _cairo_png_surface_composite_trapezoids, + _cairo_png_surface_copy_page, + _cairo_png_surface_show_page, + _cairo_png_surface_set_clip_region, + _cairo_png_surface_create_pattern +}; |