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)