[cairo] Best way to intermediate compositing, recording vs grouping

Nikita Zlobin nick87720z at gmail.com
Fri Jul 30 12:12:22 UTC 2021


I tried to get working two-stage compositing, where shaping / stiching
is separated from stacking. This is the only way to get correct result
without artifacts, which otherwise appear as result of pixels being
stacked (with OVER operator of course) after being affected by
antialiasing (shaping).

I tried two approaches - groups and recording surfaces.

For experiments I enclose tested blocks of code in if(0) like condition.

        /* Additive stiching - using recording surface */
        if (0) { /* 360 deg circle path*/
                cairo_surface_t * gs = NULL;
                cairo_t * gcr = NULL;

                /* Group compositing: opening */
                gs = cairo_recording_surface_create
                (CAIRO_CONTENT_COLOR_ALPHA, NULL); static int flag;
                if (1 || ! flag) {
                        gcr = cairo_create (gs);
                        cairo_set_operator (gcr, CAIRO_OPERATOR_ADD);
                        cairo_set_fill_rule (gcr,
                CAIRO_FILL_RULE_EVEN_ODD);

                        cairo_set_source_rgba (gcr, 0.0, 0.8, 0.0, 1.0);
                        cairo_new_path (gcr);
                        cairo_arc (gcr, 110, 112, 90, 0, M_PI*2);
                        cairo_close_path (gcr);
                        cairo_fill (gcr);

                        cairo_set_source_rgba (gcr, 0.6, 0.0, 0.6, 1.0);
                        cairo_new_path (gcr);
                        cairo_arc (gcr, 110, 112, 100, 0, M_PI*2);
                        cairo_close_path (gcr);
                        cairo_new_sub_path (gcr);
                        cairo_arc (gcr, 110, 112, 90, 0, M_PI*2);
                        cairo_close_path (gcr);
                        cairo_fill (gcr);
                        cairo_destroy (gcr);
                }
                flag = !flag;

                /* Group compositing: closing */
                cairo_set_source_surface (cr, gs, 0.0, 0.0);
                cairo_paint (cr);
                cairo_surface_destroy (gs);
        }
        /* Additive stich - using cairo groups */
        if (1) {
                /* Group compositing: opening */
                cairo_push_group (cr);
                if (1) {
                        cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
                        cairo_set_fill_rule (cr,
                CAIRO_FILL_RULE_EVEN_ODD);

                        cairo_set_source_rgba (cr, 0.0, 0.2, 0.0, 1.0);
                        cairo_new_path (cr);
                        cairo_arc (cr, 110, 112, 90, 0, M_PI*2);
                        cairo_close_path (cr);
                        cairo_fill (cr);

                        cairo_set_source_rgba (cr, 0.2, 0.0, 0.2, 1.0);
                        cairo_new_path (cr);
                        cairo_arc (cr, 110, 112, 100, 0, M_PI*2);
                        cairo_close_path (cr);
                        cairo_new_sub_path (cr);
                        cairo_arc (cr, 110, 112, 90, 0, M_PI*2);
                        cairo_close_path (cr);
                        cairo_fill (cr);
                }
                /* Group compositing: closing */
                cairo_pop_group_to_source (cr);
                cairo_paint (cr);
        }

From main loop:

        XMapWindow (display, win);
        begin_perf (60, 2, "draw()");
                surf = cairo_xlib_surface_create (
                        display, win, vinfo.visual,
                        w, h);

                cr = cairo_create (surf);
                draw (cr);
                cairo_surface_flush (surf);
                cairo_surface_show_page (surf);
                XSync (display, False);

                cairo_destroy (cr);
                cairo_surface_destroy (surf);

                sleep (1);
        end_perf;
        sleep (60);
        XUnmapWindow (display, win);

For now only groups approach works correctly.
Recodring surface is only correct for first frame. But it seems, that
when I use it next time, previous content is somehow held even despite
surf was destroyed  / created again. However, if I make it to redraw
only per 2 refreshes (see that 'flag' variable), it's correct each time
when used. Without skipping - I found no way to make new surface forget
content from "previous life" (haha). Even intentional "safeguard"
surface creation with immediate destruction (as well as cairo_paint()
with CLEAR operator... does rec surf ever interpret this?).

Main surface with its drawer was preserved initially (in main loop),
but I also tried to limit its lifetime as some programs do (not that it
had any effect).

I initially assumed that recording surfaces could be more effective
than automatic groups, since groups are created each time, yet I can't
imagine, how would it do such correct compositing without image buffer,
unless it somehow joins OVER-ed layers into single, which I can't
remember mathematically possible.

At least rec surface could be reused - if only I was sure, that it can
be cleared instead of just recording clear command.

Now I'm not sure:
- does it ever make sence to prefer recording surfaces over groups?
  (i.e., can if have better performance)
- and is it possible to reliably clear at least for next refresh?
  (as described above)


More information about the cairo mailing list