svgcolorscairolibrsvg

C++ Cairo + RSVG - Set SVG colour when drawing


I'm trying to draw a single colour SVG onto a Cairo surface but it is being rendered in white where I'd like it to be black. The Cairo surface is an 8-bit alpha surface (CAIRO_FORMAT_A8) so I'm only dealing with grey scale.

The image I'm testing it on is the following: https://commons.wikimedia.org/wiki/File:Octicons-flame.svg

This is the code I'm using to render:

//Display buffer and Canvas
int stride;
Byte *displayBuffer;
cairo_surface_t *surface;

stride = cairo_format_stride_for_width (CAIRO_FORMAT_A8, gulPanelW);
displayBuffer = malloc (stride * gulPanelH);
surface = cairo_image_surface_create_for_data (displayBuffer, CAIRO_FORMAT_A8, gulPanelW, gulPanelH, stride);
cairo_t *cr = cairo_create (surface);

cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);

//Flame Surface and SVG Handle
RsvgHandle* flame_handle;
flame_handle = rsvg_handle_new_from_file("flame.svg", 0);

//Set canvas background to grey
cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.5);
cairo_paint (cr);

//Add initial data and output
cairo_select_font_face (cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size (cr, 48.0);
cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0);
cairo_move_to (cr, 700.0, 105.0);
cairo_rotate(cr, (M_PI/2.0));
cairo_show_text (cr, "Temperature");
cairo_rotate(cr, -(M_PI/2.0));

cairo_select_font_face (cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size (cr, 200.0);
cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0);
cairo_move_to (cr, 540.0, 34.0);
cairo_rotate(cr, (M_PI/2.0));
cairo_show_text (cr, "19.4");
cairo_rotate(cr, -(M_PI/2.0));

cairo_translate (cr, 476.0, 200.0);
cairo_scale (cr, 0.07, 0.07);
cairo_rotate(cr, (M_PI/2.0));
rsvg_handle_render_cairo (flame_handle, cr);
cairo_rotate (cr, -(M_PI/2.0));
cairo_scale (cr, 1.0, 1.0);

cairo_surface_write_to_png (surface, "output.png");

This outputs the following:

Cairo Output

But as you can see, the flame is rendered in white where I'd like it to be a black - or a specific alpha value.

Any help would be appreciated, I can't find much information on SVG colours in Cairo.

Many thanks.


Solution

  • cairo_mask and cairo_mask_surface can use the alpha channel of another surface as the mask for a drawing operation. The following example uses this to "draw things" with a specific alpha level.

    I also added the necessary things to make this a self-contained example and got rid of the unnecessary cairo_image_surface_create_for_data.

    #include <cairo.h>
    #include <librsvg/rsvg.h>
    #include <math.h>
    
    #define gulPanelW 800
    #define gulPanelH 480
    
    int main()
    {
        //Display buffer and Canvas
        cairo_surface_t *surface;
    
        surface = cairo_image_surface_create (CAIRO_FORMAT_A8, gulPanelW, gulPanelH);
        cairo_t *cr = cairo_create (surface);
    
        cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
    
        //Flame Surface and SVG Handle
        RsvgHandle* flame_handle;
        flame_handle = rsvg_handle_new_from_file("flame.svg", 0);
    
        //Set canvas background to grey
        cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.5);
        cairo_paint (cr);
    
        //Add initial data and output
        cairo_select_font_face (cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
        cairo_set_font_size (cr, 48.0);
        cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0);
        cairo_move_to (cr, 700.0, 105.0);
        cairo_rotate(cr, (M_PI/2.0));
        cairo_show_text (cr, "Temperature");
        cairo_rotate(cr, -(M_PI/2.0));
    
        cairo_select_font_face (cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
        cairo_set_font_size (cr, 200.0);
        cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0);
        cairo_move_to (cr, 540.0, 34.0);
        cairo_rotate(cr, (M_PI/2.0));
        cairo_show_text (cr, "19.4");
        cairo_rotate(cr, -(M_PI/2.0));
    
        // Draw the flame icon to a temporary surface
        cairo_save (cr);
        cairo_translate (cr, 476.0, 200.0);
        cairo_scale (cr, 0.07, 0.07);
        cairo_rotate(cr, (M_PI/2.0));
        cairo_push_group (cr);
        rsvg_handle_render_cairo (flame_handle, cr);
        cairo_pattern_t *p = cairo_pop_group (cr);
    
        // Use the temporary surface as a mask for drawing this color
        cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0);
        cairo_mask(cr, p);
        cairo_restore (cr);
    
        // Clean up
        cairo_surface_write_to_png (surface, "output.png");
        cairo_pattern_destroy (p);
        cairo_destroy (cr);
        cairo_surface_destroy (surface);
        return 0;
    }