0

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.

  • What's the difference between the top and bottom rendering? Put differently: If you know how not to get these colors already, what is the problem? – Uli Schlachter Mar 17 '18 at 10:04
  • The problem is that I want to have an intermediate recording surface so I can do postprocessing on it without affecting my final surface. I _could probably_ get rid of it, but that would vastly complicate my code. – Zombie Feynman Mar 19 '18 at 13:43

2 Answers2

1

Turns out @uli-schlachter is almost correct: you have to set the antialiasing options on a Pango layout object:

PangoContext *pctx = pango_layout_get_context(layout);
cairo_font_options_t *opts = cairo_font_options_create();
cairo_font_options_set_antialias(opts, CAIRO_ANTIALIAS_GRAY);
pango_cairo_context_set_font_options(pctx, opts);
cairo_font_options_destroy(opts);
0

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);
Uli Schlachter
  • 9,337
  • 1
  • 23
  • 39
  • Interesting. Unfortunately this does not make any difference for me, but if you get something different, it may be a bug in cairo/pango/pangocairo after all. – Zombie Feynman Mar 19 '18 at 13:28