Converting Cairo output to PNG
This is an example program of converting the output of Cairo into a
PNG using the C PNG library libpng without using the Cairo PNG
writing functions. The PNG writing code is much the same as another
example on this site, Write a PNG file using C and libpng. This program creates an image
like the one on the right called file.png.
In this example, the PNG input data is pointed directly to the
Cairo output data and then transformed using the libpng
option PNG_TRANSFORM_BGR.
I did this because I wanted to add text information to the PNG output by Cairo. I originally thought there would be a way to add information using Cairo's interface, but according to the answer to a query on the Cairo mailing list, no such ability exists. See also Create a PNG with text segments.
/* This example program shows how to write the data output by the Cairo library into a PNG file without using the Cairo library's PNG-writing functions, using the libpng functions alone. */ #include <stdlib.h> #include <png.h> #include <cairo.h> #include <stdio.h> #include <stdint.h> #include <string.h> #include <errno.h> typedef struct { /* The image data. */ unsigned char * data; int height; int width; /* The number of bytes in one pixel. */ int pixel_bytes; } bitmap_t; #define CLEAN_UP \ if (png_ptr) { \ png_destroy_write_struct (& png_ptr, & info_ptr); \ } \ if (row_pointers) { \ free (row_pointers); \ } \ if (fp) { \ fclose (fp); \ } #define FAIL(test) \ if (test) { \ fprintf (stderr, "%s:%d: failed test '%s'", \ __FILE__, __LINE__, #test); \ CLEAN_UP; \ return -1; \ } /* Write "bitmap" to a PNG file specified by "path". This function returns 0 on success, and non-zero on error. This is based on the code at 'http://www.lemoda.net/png/c-write-png/' */ int save_png_to_file (const bitmap_t * bitmap, const char *path) { FILE * fp; png_structp png_ptr = NULL; png_infop info_ptr = NULL; size_t y; png_byte ** row_pointers = NULL; int depth = 8; fp = fopen (path, "wb"); FAIL (! fp); png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); FAIL (! png_ptr); info_ptr = png_create_info_struct (png_ptr); FAIL (! info_ptr); /* Set up error handling. */ if (setjmp (png_jmpbuf (png_ptr))) { /* We arrive here if an error occurred in the PNG library. */ fprintf (stderr, "An error occurred in the PNG library.\n"); CLEAN_UP; return -1; } /* Set image attributes. */ png_set_IHDR (png_ptr, info_ptr, bitmap->width, bitmap->height, depth, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); /* Initialize rows of PNG. */ row_pointers = malloc (bitmap->height * sizeof (png_byte *)); FAIL (! row_pointers); for (y = 0; y < bitmap->height; ++y) { row_pointers[y] = bitmap->data + bitmap->width * bitmap->pixel_bytes * y; } /* Write the image data to "fp". */ png_init_io (png_ptr, fp); png_set_rows (png_ptr, info_ptr, row_pointers); /* The option PNG_TRANSFORM_BGR is necessary to convert the Cairo format for the red, green, blue data into the PNG format. */ png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_BGR, NULL); CLEAN_UP; return 0; } int main () { int SIZEX = 80; int SIZEY = 80; char * fname = "file.png"; cairo_t *c; cairo_surface_t *cs; bitmap_t bitmap; int rv; cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, SIZEX, SIZEY); c = cairo_create (cs); /* Draw some coloured rectangles in the image. */ cairo_rectangle (c, 2.5, 0.0, SIZEX - 1.0, SIZEY / 2.0); cairo_set_source_rgb (c, 0.1, 0.0, 0.7); cairo_fill (c); cairo_rectangle (c, 0.0, SIZEY / 2.0, SIZEX / 2.0, SIZEY / 2.0); cairo_set_source_rgb (c, 1.0, 1.0, 0.0); cairo_fill (c); cairo_rectangle (c, SIZEX / 3.0, SIZEY / 3.0, (SIZEX) / 3.0, (SIZEY) / 3.0); cairo_set_source_rgb (c, 1.0, 0.0, 0.0); cairo_fill (c); /* We have to call this before reading the data. */ cairo_surface_flush (cs); bitmap.data = cairo_image_surface_get_data (cs); bitmap.pixel_bytes = 4; bitmap.width = SIZEX; bitmap.height = SIZEY; rv = save_png_to_file (& bitmap, fname); if (rv != 0) { fprintf (stderr, "Failed to write PNG to file.\n"); } cairo_surface_destroy (cs); return 0; }
Here is a makefile to compile this on a Unix-like system:
CAIRO_LD_FLAGS=`pkg-config --libs cairo` PNG_LD_FLAGS=-L /usr/local/lib -l png CAIRO_INC_FLAGS=`pkg-config --cflags cairo` PNG_INC_FLAGS=-I /usr/local/include CFLAGS=-Wall -g OBJS=cairo-png.o file.png: cairo-png ./cairo-png cairo-png: $(OBJS) $(CC) $(CFLAGS) -o $@ $(OBJS) $(CAIRO_LD_FLAGS) $(PNG_LD_FLAGS) cairo-png.o: cairo-png.c $(CC) $(CFLAGS) -c -o $@ cairo-png.c $(CAIRO_INC_FLAGS) $(PNG_INC_FLAGS) clean: rm -f file.png cairo-png $(OBJS)