cairopangopangocairo

pangocairo: discolouration on transparent recording surfaces


When I use Pango to draw text onto a transparent Cairo recording surface, the text becomes quite heavily discoloured, particularly in the semi-transparent anti-aliased regions. For example, this image has the correct (top) and incorrect (bottom) rendition of some text. (Segment of previous image zoomed in).

My question, obviously, is how to get the correct colour rendition when using a transparent recording surface. At least, I am assuming this problem has to do with me not understanding the pangocairo pipeline very well. If it's a bug in Pango or Cairo, please do let me know.

Code used to generate the image above:

#include <cairo/cairo.h>
#include <pango/pangocairo.h>

int main(int argc, char **argv)
{
    cairo_surface_t *canvas = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
        320, 60);
    cairo_t *cctx = cairo_create(canvas);

    cairo_surface_t *surf = cairo_recording_surface_create(
        CAIRO_CONTENT_COLOR_ALPHA, NULL);
    cairo_t *ctx = cairo_create(surf);
    PangoFontDescription *fd = pango_font_description_new();
    PangoLayout *layout = pango_cairo_create_layout(ctx);
    PangoAttrList *attrs = pango_attr_list_new();
    PangoAttribute *at;

    cairo_set_source_rgba(cctx, 1, 1, 1, 1);
    cairo_paint(cctx);
    cairo_translate(cctx, 16, 8);

    pango_font_description_set_family(fd, "DejaVu Sans Mono");
    pango_layout_set_font_description(layout, fd);

    pango_layout_set_text(layout, "Bless the Maker and His water", -1);
    pango_layout_set_attributes(layout, attrs);

    at = pango_attr_foreground_new(0, 0xffff, 0xffff);
    pango_attr_list_change(attrs, at);
    at = pango_attr_foreground_alpha_new(0xffff);
    pango_attr_list_change(attrs, at);

    cairo_save(ctx);
    cairo_set_source_rgba(ctx, 1, 1, 1, 1);
    cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
    cairo_paint(ctx);
    cairo_restore(ctx);

    pango_cairo_update_layout(ctx, layout);
    pango_cairo_show_layout(ctx, layout);

    cairo_set_source_surface(cctx, surf, 0, 0);
    cairo_paint(cctx);

    cairo_save(ctx);
    cairo_set_source_rgba(ctx, 0, 0, 0, 0);
    cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
    cairo_paint(ctx);
    cairo_restore(ctx);

    pango_cairo_update_layout(ctx, layout);
    pango_cairo_show_layout(ctx, layout);

    cairo_translate(cctx, 0, 24);
    cairo_set_source_surface(cctx, surf, 0, 0);
    cairo_paint(cctx);

    cairo_surface_write_to_png(canvas, "test.png");

    g_object_unref(layout);
    pango_attr_list_unref(attrs);
    pango_font_description_free(fd);
    cairo_destroy(ctx);
    cairo_destroy(cctx);
    cairo_surface_destroy(surf);
    cairo_surface_destroy(canvas);

    return 0;
}


Update: this seems to be caused by something in the video pipeline, and is probably a bug. I can reproduce this on two boxes using AMD video cards, but not on one with an nVidia card. I'm leaving this open in case anybody knows a fix.


Solution

  • First: You example code does not reproduce the problem here. There are no "color fringes" in the resulting image.

    If you allow me to guess: Cairo is assuming a subpixel order of RGB for you. According to the documentation for cairo_subpixel_order_t this is only used with CAIRO_ANTIALIAS_SUBPIXEL. Thus, I would suggest you to do:

    cairo_font_options_t *opts = cairo_get_font_options(some_cairo_context);
    cairo_font_options_set_antialias(opts, CAIRO_ANTIALIAS_GRAY); // or _NONE
    cairo_set_font_options(some_cairo_context, opts);
    cairo_font_options_destroy(opts);