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.

#include <stdlib.h>
#include <png.h>
#include <cairo.h>
#include <stdio.h>
#include <stdint.h>

typedef struct {
    unsigned char * data;
    int height;
    int width;
    int pixel_bytes;
} bitmap_t;

/* Write "bitmap" to a PNG file specified by "path"; returns 0 on
   success, 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 x, y;
    png_byte ** row_pointers = NULL;
    int status = -1;
    int depth = 8;
    
    fp = fopen (path, "wb");
    if (! fp) {
        goto fopen_failed;
    }

    png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (png_ptr == NULL) {
        goto png_create_write_struct_failed;
    }
    
    info_ptr = png_create_info_struct (png_ptr);
    if (info_ptr == NULL) {
        goto png_create_info_struct_failed;
    }
    
    /* Set up error handling. */

    if (setjmp (png_jmpbuf (png_ptr))) {
        goto png_failure;
    }
    
    /* 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 *));
    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);
    png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_BGR, NULL);

    /* The routine has successfully written the file, so we set
       "status" to a value which indicates success. */

    status = 0;
    
    free (row_pointers);
    
 png_failure:
 png_create_info_struct_failed:
    png_destroy_write_struct (&png_ptr, &info_ptr);
 png_create_write_struct_failed:
    fclose (fp);
 fopen_failed:
    return status;
}


int main ()
{
    int SIZEX = 80;
    int SIZEY = 80;
    char * fname = "file.png";
    cairo_t *c;
    cairo_surface_t *cs;
    cairo_status_t status;
    int depth;
    int i;
    int j;
    bitmap_t bitmap;

    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;
    save_png_to_file (& bitmap, fname);
    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

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)
Ask and answer questions on C in the new C forum

Copyright © Ben Bullock 2009-2012. All rights reserved. For comments, questions, and corrections, please email Ben Bullock (ben.bullock@lemoda.net) / Privacy / Disclaimer