Reputation: 672
While using Cairo 1.14.6 for display purposes, I found that overwriting the very same path with another color does not necessarily overwrite all pixels, and leaves undesirable artifacts behind.
As evidence of my claim I offer this output from a short self-contained example, the source for which follows further below.
An explanation of the six parts of the image, from left to right:
The image was generated from the following code:
#include "cairo/cairo.h"
#define M_PI 3.14159265358979323846
void draw_shape(cairo_t* cr, int x, int y) {
cairo_arc(cr, 50 + x, 50 + y, 48, -M_PI, -M_PI / 2);
cairo_stroke(cr);
cairo_move_to(cr, x + 2, y + 2);
cairo_line_to(cr, x + 48, y + 48);
cairo_stroke(cr);
}
int main(int argc, char** argv) {
int x = 0;
int y = 0;
cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 300, 50);
cairo_t* cr = cairo_create(surface);
/* Draw a white background and a few shapes to overwrite */
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
cairo_paint(cr);
cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 1.0);
draw_shape(cr, x, y); x += 50;
draw_shape(cr, x, y); x += 50;
draw_shape(cr, x, y); x += 50;
draw_shape(cr, x, y); x += 50;
draw_shape(cr, x, y); x += 50;
draw_shape(cr, x, y); x += 50;
x = 50;
/* Leftmost shape is left unchanged for reference */
/* Stroke in RGBA opaque white */
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
draw_shape(cr, x, y); x += 50;
/* Stroke in RGB white */
cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
draw_shape(cr, x + 0, y); x += 50;
/* Stroke in opaque white without blending */
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
draw_shape(cr, x, y); x += 50;
/* Stroke in opaque white without blending, with no antialiasing */
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
draw_shape(cr, x, y); x += 50;
/* Stroke in opaque white without blending, with best antialiasing */
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_set_antialias(cr, CAIRO_ANTIALIAS_BEST);
draw_shape(cr, x, y); x += 50;
/* Write the results to a file */
cairo_surface_write_to_png(surface, "output.png");
return 0;
}
It doesn't make intuitive sense to me that overwriting the very same shape would not overwrite all of its pixels, especially if I force it into non-blending CAIRO_OPERATOR_SOURCE mode. The results are the same on the framebuffer that constitutes my actual surface, so this is not an issue with the backend.
Cairo is usually so good at what it does that I'm very surprised at this. Is there no way to overwrite an anti-aliased shape exactly in Cairo?
Upvotes: 0
Views: 347
Reputation: 672
What I was trying to do is apparently not possible. I posted my question to the Cairo mailing list and was offered two options:
More specifically:
All that is stored in the pixel from the first drawing is what percentage of the pixel was covered by the shape. It does not remember exactly what parts of the pixel are covered.
Since antialiasing necessarily involves blending, and since Cairo does not remember what part of a subpixel led to the blending, it has no way of knowing how to undo that blending.
Upvotes: 3