Search code examples
cairo

Cairo - overlapping lines - "deadzone" with no draw


I have multiple lines in Cairo. I want to have a "deadzone" around the line, so if I render multiple overlapping lines, they are not overlapping.

Something line on image below.

enter image description here


Solution

  • Here is a sample program that draws the later lines first in white with a larger line width and then again in black with the actual line width:

    #include <cairo.h>
    
    int main()
    {
        cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 200, 200);
        cairo_t *cr = cairo_create(s);
    
        cairo_set_source_rgb(cr, 1, 1, 1);
        cairo_paint(cr);
    
        cairo_move_to(cr, 50, 20);
        cairo_line_to(cr, 110, 160);
    
        cairo_set_line_width(cr, 4);
        cairo_set_source_rgb(cr, 0, 0, 0);
        cairo_stroke(cr);
    
    
        cairo_move_to(cr, 200, 0);
        cairo_line_to(cr, 60, 160);
    
        cairo_set_line_width(cr, 15);
        cairo_set_source_rgb(cr, 1, 1, 1);
        cairo_stroke_preserve(cr);
    
        cairo_set_line_width(cr, 4);
        cairo_set_source_rgb(cr, 0, 0, 0);
        cairo_stroke(cr);
    
    
        cairo_move_to(cr, 10, 100);
        cairo_line_to(cr, 220, 50);
    
        cairo_set_line_width(cr, 15);
        cairo_set_source_rgb(cr, 1, 1, 1);
        cairo_stroke_preserve(cr);
    
        cairo_set_line_width(cr, 4);
        cairo_set_source_rgb(cr, 0, 0, 0);
        cairo_stroke(cr);
    
        cairo_surface_write_to_png(s, "out.png");
    
        cairo_destroy(cr);
        cairo_surface_destroy(s);
    }
    

    Output:

    Output of the sample program

    Okay, now to the background. Option 1 is to draw the lines with a transparent background and then draw the background below that via operator DEST_OVER:

    #include <cairo.h>
    
    int main()
    {
        /* I changed this to ARGB32 */
        cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 200, 200);
        cairo_t *cr = cairo_create(s);
        cairo_pattern_t *p = cairo_pattern_create_linear(0, 0, 200, 200);
    
        /* Draw the lines */
    
        cairo_move_to(cr, 50, 20);
        cairo_line_to(cr, 110, 160);
    
        cairo_set_line_width(cr, 4);
        cairo_set_source_rgb(cr, 0, 0, 0);
        cairo_stroke(cr);
    
    
        cairo_move_to(cr, 200, 0);
        cairo_line_to(cr, 60, 160);
    
        cairo_set_line_width(cr, 15);
        /* I changed all "drawing white" to "drawing transparency" */
        cairo_save(cr);
        cairo_set_source_rgba(cr, 0, 0, 0, 0);
        cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
        cairo_stroke_preserve(cr);
        cairo_restore(cr);
    
        cairo_set_line_width(cr, 4);
        cairo_set_source_rgb(cr, 0, 0, 0);
        cairo_stroke(cr);
    
    
        cairo_move_to(cr, 10, 100);
        cairo_line_to(cr, 220, 50);
    
        cairo_set_line_width(cr, 15);
        cairo_save(cr);
        cairo_set_source_rgba(cr, 0, 0, 0, 0);
        cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
        cairo_stroke_preserve(cr);
        cairo_restore(cr);
    
        cairo_set_line_width(cr, 4);
        cairo_set_source_rgb(cr, 0, 0, 0);
        cairo_stroke(cr);
    
        /* Draw a background below what we drew so far */
        cairo_pattern_add_color_stop_rgb(p, 0, 1, 0, 0);
        cairo_pattern_add_color_stop_rgb(p, 1, 0, 0, 1);
        cairo_set_source(cr, p);
        /* Actually, I mean DEST_OVER instead of ATOP (which I wrote in my comment) */
        cairo_set_operator(cr, CAIRO_OPERATOR_DEST_OVER);
        cairo_paint(cr);
    
        cairo_surface_write_to_png(s, "out.png");
    
        cairo_pattern_destroy(p);
        cairo_destroy(cr);
        cairo_surface_destroy(s);
    }
    

    Output:

    Same image with a background

    Option 2 is to use a temporary surface. The lines are drawn with a transparent background to this. Afterwards, this is drawn ontop of the existing background:

    #include <cairo.h>
    
    int main()
    {
        cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 200, 200);
        cairo_t *cr = cairo_create(s);
        cairo_pattern_t *p = cairo_pattern_create_linear(0, 0, 200, 200);
    
        /* Draw a background */
        cairo_pattern_add_color_stop_rgb(p, 0, 1, 0, 0);
        cairo_pattern_add_color_stop_rgb(p, 1, 0, 0, 1);
        cairo_set_source(cr, p);
        cairo_paint(cr);
        cairo_pattern_destroy(p);
    
        /* Draw the lines */
    
        /* but draw them to a temporary surface */
        cairo_push_group_with_content(cr, CAIRO_CONTENT_COLOR_ALPHA);
    
        cairo_move_to(cr, 50, 20);
        cairo_line_to(cr, 110, 160);
    
        cairo_set_line_width(cr, 4);
        cairo_set_source_rgb(cr, 0, 0, 0);
        cairo_stroke(cr);
    
    
        cairo_move_to(cr, 200, 0);
        cairo_line_to(cr, 60, 160);
    
        cairo_set_line_width(cr, 15);
        /* I changed all "drawing white" to "drawing transparency" */
        cairo_save(cr);
        cairo_set_source_rgba(cr, 0, 0, 0, 0);
        cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
        cairo_stroke_preserve(cr);
        cairo_restore(cr);
    
        cairo_set_line_width(cr, 4);
        cairo_set_source_rgb(cr, 0, 0, 0);
        cairo_stroke(cr);
    
    
        cairo_move_to(cr, 10, 100);
        cairo_line_to(cr, 220, 50);
    
        cairo_set_line_width(cr, 15);
        cairo_save(cr);
        cairo_set_source_rgba(cr, 0, 0, 0, 0);
        cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
        cairo_stroke_preserve(cr);
        cairo_restore(cr);
    
        cairo_set_line_width(cr, 4);
        cairo_set_source_rgb(cr, 0, 0, 0);
        cairo_stroke(cr);
    
        /* Now draw the temporary surface to the target surface */
        cairo_pop_group_to_source(cr);
        cairo_paint(cr);
    
        cairo_surface_write_to_png(s, "out.png");
    
        cairo_destroy(cr);
        cairo_surface_destroy(s);
    }
    

    Output:

    another way to draw this on some background