无法覆盖开罗的抗锯齿像素

Failing to overwrite antialiased pixels in Cairo

在使用 Cairo 1.14.6 进行显示时,我发现用另一种颜色覆盖完全相同的路径并不一定会覆盖所有像素,并且会留下不需要的伪像。

作为我的主张的证据,我提供了一个简短的独立示例的输出,其来源如下。

图片的六个部分的解释,从左到右:

  1. 原始形状用蓝色描边。
  2. 以 RGBA 白色覆盖原始形状。
  3. 原始形状被 RGB 白色覆盖。
  4. 使用 CAIRO_OPERATOR_SOURCE 模式以 RGBA 白色覆盖原始形状。
  5. 使用 CAIRO_OPERATOR_SOURCE 模式和 CAIRO_ANTIALIAS_NONE.
  6. 以 RGBA 白色覆盖原始形状
  7. 使用 CAIRO_OPERATOR_SOURCE 模式和 CAIRO_ANTIALIAS_BEST.
  8. 以 RGBA 白色覆盖原始形状

图像由以下代码生成:

#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;
}

直觉上我认为覆盖完全相同的形状不会覆盖其所有像素,尤其是当我强制其进入非混合 CAIRO_OPERATOR_SOURCE 模式时。结果在构成我的实际表面的帧缓冲区上是相同的,所以这不是后端的问题。

开罗通常非常擅长它所做的事情,我对此感到非常惊讶。有没有办法在开罗完全覆盖抗锯齿形状?

我试图做的事情显然是不可能的。我将 question 发布到 Cairo 邮件列表并获得了两个选项:

  1. 在绘制之前保留原始像素的副本:"Anti-aliasing involves blending. If you don’t want anti-aliasing, turn it off." (Link)
  2. 以更高的分辨率绘制:"The only real solution is to draw a much higher resolution with coverage rounded to exactly zero or one for each pixel." (Link)

更具体地说:

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.

由于抗锯齿必然涉及混合,并且由于 Cairo 不记得导致混合的子像素的哪个部分,因此它无法知道如何撤消该混合。