Exemplo n.º 1
0
void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
{
    if (paintingDisabled())
        return;

    if (npoints <= 1)
        return;

    cairo_t* cr = platformContext()->cr();

    cairo_save(cr);
    cairo_set_antialias(cr, shouldAntialias ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
    addConvexPolygonToContext(cr, npoints, points);

    if (fillColor().alpha()) {
        setSourceRGBAFromColor(cr, fillColor());
        cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
        cairo_fill_preserve(cr);
    }

    if (strokeStyle() != NoStroke) {
        setSourceRGBAFromColor(cr, strokeColor());
        cairo_set_line_width(cr, strokeThickness());
        cairo_stroke(cr);
    } else
        cairo_new_path(cr);

    cairo_restore(cr);
}
Exemplo n.º 2
0
// This method is only used to draw the little circles used in lists.
void GraphicsContext::drawEllipse(const IntRect& rect)
{
    if (paintingDisabled())
        return;

    cairo_t* cr = platformContext()->cr();
    cairo_save(cr);
    float yRadius = .5 * rect.height();
    float xRadius = .5 * rect.width();
    cairo_translate(cr, rect.x() + xRadius, rect.y() + yRadius);
    cairo_scale(cr, xRadius, yRadius);
    cairo_arc(cr, 0., 0., 1., 0., 2 * piFloat);
    cairo_restore(cr);

    if (fillColor().alpha()) {
        setSourceRGBAFromColor(cr, fillColor());
        cairo_fill_preserve(cr);
    }

    if (strokeStyle() != NoStroke) {
        setSourceRGBAFromColor(cr, strokeColor());
        cairo_set_line_width(cr, strokeThickness());
        cairo_stroke(cr);
    } else
        cairo_new_path(cr);
}
static void prepareCairoContextSource(cairo_t* cr, Pattern* pattern, Gradient* gradient, const Color& color, float globalAlpha)
{
    if (pattern) {
        RefPtr<cairo_pattern_t> cairoPattern(adoptRef(pattern->createPlatformPattern(AffineTransform())));
        cairo_set_source(cr, cairoPattern.get());
        reduceSourceByAlpha(cr, globalAlpha);
    } else if (gradient)
        cairo_set_source(cr, gradient->platformGradient(globalAlpha));
    else { // Solid color source.
        if (globalAlpha < 1)
            setSourceRGBAFromColor(cr, colorWithOverrideAlpha(color.rgb(), color.alpha() / 255.f * globalAlpha));
        else
            setSourceRGBAFromColor(cr, color);
    }
}
Exemplo n.º 4
0
void GraphicsLayerTextureMapper::drawRepaintCounter(GraphicsContext* context)
{
    cairo_t* cr = context->platformContext()->cr();
    cairo_save(cr);

    CString repaintCount = String::format("%i", this->repaintCount()).utf8();
    cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
    cairo_set_font_size(cr, 18);

    cairo_text_extents_t repaintTextExtents;
    cairo_text_extents(cr, repaintCount.data(), &repaintTextExtents);

    static const int repaintCountBorderWidth = 10;
    setSourceRGBAFromColor(cr, isShowingDebugBorder() ? m_debugBorderColor : Color(0, 255, 0, 127));
    cairo_rectangle(cr, 0, 0,
        repaintTextExtents.width + (repaintCountBorderWidth * 2),
        repaintTextExtents.height + (repaintCountBorderWidth * 2));
    cairo_fill(cr);

    cairo_set_source_rgb(cr, 1, 1, 1);
    cairo_move_to(cr, repaintCountBorderWidth, repaintTextExtents.height + repaintCountBorderWidth);
    cairo_show_text(cr, repaintCount.data());

    cairo_restore(cr);
}
static void drawGlyphsShadow(GraphicsContext* graphicsContext, const FloatPoint& point, const SimpleFontData* font, GlyphBufferGlyph* glyphs, int numGlyphs)
{
    ShadowBlur& shadow = graphicsContext->platformContext()->shadowBlur();

    if (!(graphicsContext->textDrawingMode() & TextModeFill) || shadow.type() == ShadowBlur::NoShadow)
        return;

    if (!graphicsContext->mustUseShadowBlur()) {
        // Optimize non-blurry shadows, by just drawing text without the ShadowBlur.
        cairo_t* context = graphicsContext->platformContext()->cr();
        cairo_save(context);

        FloatSize shadowOffset(graphicsContext->state().shadowOffset);
        cairo_translate(context, shadowOffset.width(), shadowOffset.height());
        setSourceRGBAFromColor(context, graphicsContext->state().shadowColor);
        drawGlyphsToContext(context, font, glyphs, numGlyphs);

        cairo_restore(context);
        return;
    }

    cairo_text_extents_t extents;
    cairo_scaled_font_glyph_extents(font->platformData().scaledFont(), glyphs, numGlyphs, &extents);
    FloatRect fontExtentsRect(point.x() + extents.x_bearing, point.y() + extents.y_bearing, extents.width, extents.height);

    if (GraphicsContext* shadowContext = shadow.beginShadowLayer(graphicsContext, fontExtentsRect)) {
        drawGlyphsToContext(shadowContext->platformContext()->cr(), font, glyphs, numGlyphs);
        shadow.endShadowLayer(graphicsContext);
    }
}
Exemplo n.º 6
0
// A helper which quickly fills a rectangle with a simple color fill.
static inline void fillRectWithColor(cairo_t* cr, const FloatRect& rect, const Color& color)
{
    if (!color.alpha() && cairo_get_operator(cr) == CAIRO_OPERATOR_OVER)
        return;
    setSourceRGBAFromColor(cr, color);
    cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
    cairo_fill(cr);
}
Exemplo n.º 7
0
void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
{
    if (paintingDisabled() || strokeStyle() == NoStroke)
        return;

    int x = rect.x();
    int y = rect.y();
    float w = rect.width();
    float h = rect.height();
    float scaleFactor = h / w;
    float reverseScaleFactor = w / h;

    float hRadius = w / 2;
    float vRadius = h / 2;
    float fa = startAngle;
    float falen =  fa + angleSpan;

    cairo_t* cr = platformContext()->cr();
    cairo_save(cr);

    if (w != h)
        cairo_scale(cr, 1., scaleFactor);

    cairo_arc_negative(cr, x + hRadius, (y + vRadius) * reverseScaleFactor, hRadius, -fa * M_PI/180, -falen * M_PI/180);

    if (w != h)
        cairo_scale(cr, 1., reverseScaleFactor);

    int patternWidth = 0;
    switch (strokeStyle()) {
    case DottedStroke:
        patternWidth = floorf(strokeThickness() / 2.f);
        break;
    case DashedStroke:
        patternWidth = 3 * floorf(strokeThickness() / 2.f);
        break;
    default:
        break;
    }

    setSourceRGBAFromColor(cr, strokeColor());

    if (patternWidth) {
        float distance = 0;
        if (hRadius == vRadius)
            distance = (piFloat * hRadius) / 2.f;
        else // We are elliptical and will have to estimate the distance
            distance = (piFloat * sqrtf((hRadius * hRadius + vRadius * vRadius) / 2.f)) / 2.f;
        double patternOffset = calculateStrokePatternOffset(floorf(distance), patternWidth);
        double patternWidthAsDouble = patternWidth;
        cairo_set_dash(cr, &patternWidthAsDouble, 1, patternOffset);
    }

    cairo_stroke(cr);
    cairo_restore(cr);
}
Exemplo n.º 8
0
static void prepareCairoContextSource(cairo_t* cr, Pattern* pattern, Gradient* gradient, const Color& color, float globalAlpha)
{
    if (pattern) {
        RefPtr<cairo_pattern_t> cairoPattern(adoptRef(pattern->createPlatformPattern(AffineTransform())));
        cairo_set_source(cr, cairoPattern.get());
        reduceSourceByAlpha(cr, globalAlpha);
    } else if (gradient) {
        cairo_set_source(cr, gradient->platformGradient());

        // FIXME: It would be faster to simply recreate the Cairo gradient and multiply the
        // color stops by the global alpha.
        reduceSourceByAlpha(cr, globalAlpha);
    } else { // Solid color source.
        if (globalAlpha < 1)
            setSourceRGBAFromColor(cr, colorWithOverrideAlpha(color.rgb(), color.alpha() / 255.f * globalAlpha));
        else
            setSourceRGBAFromColor(cr, color);
    }
}
Exemplo n.º 9
0
static void drawGlyphsShadow(GraphicsContext* graphicsContext, const FloatPoint& point, PangoLayoutLine* layoutLine, PangoRegionType renderRegion)
{
    ShadowBlur& shadow = graphicsContext->platformContext()->shadowBlur();

    if (!(graphicsContext->textDrawingMode() & TextModeFill) || shadow.type() == ShadowBlur::NoShadow)
        return;

    FloatPoint totalOffset(point + graphicsContext->state().shadowOffset);

    // Optimize non-blurry shadows, by just drawing text without the ShadowBlur.
    if (!shadow.mustUseShadowBlur(graphicsContext)) {
        cairo_t* context = graphicsContext->platformContext()->cr();
        cairo_save(context);
        cairo_translate(context, totalOffset.x(), totalOffset.y());

        setSourceRGBAFromColor(context, graphicsContext->state().shadowColor);
#if PLATFORM(GTK)
        gdk_cairo_region(context, renderRegion);
#else
        appendRegionToCairoContext(context, renderRegion);
#endif
        cairo_clip(context);
        pango_cairo_show_layout_line(context, layoutLine);

        cairo_restore(context);
        return;
    }

    FloatRect extents(getPangoRegionExtents(renderRegion));
    extents.setLocation(FloatPoint(point.x(), point.y() - extents.height()));
    if (GraphicsContext* shadowContext = shadow.beginShadowLayer(graphicsContext, extents)) {
        cairo_t* cairoShadowContext = shadowContext->platformContext()->cr();
        cairo_translate(cairoShadowContext, point.x(), point.y());
        pango_cairo_show_layout_line(cairoShadowContext, layoutLine);

        // We need the clipping region to be active when we blit the blurred shadow back,
        // because we don't want any bits and pieces of characters out of range to be
        // drawn. Since ShadowBlur expects a consistent transform, we have to undo the
        // translation before calling endShadowLayer as well.
        cairo_t* context = graphicsContext->platformContext()->cr();
        cairo_save(context);
        cairo_translate(context, totalOffset.x(), totalOffset.y());
#if PLATFORM(GTK)
        gdk_cairo_region(context, renderRegion);
#else
        appendRegionToCairoContext(context, renderRegion);
#endif
        cairo_clip(context);
        cairo_translate(context, -totalOffset.x(), -totalOffset.y());

        shadow.endShadowLayer(graphicsContext);
        cairo_restore(context);
    }
}
static cairo_surface_t* checkeredSurface()
{
    static cairo_surface_t* surface = 0;
    if (surface)
        return surface;

    surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, checkerSize, checkerSize);
    RefPtr<cairo_t> cr = adoptRef(cairo_create(surface));

    unsigned halfCheckerSize = checkerSize / 2;
    setSourceRGBAFromColor(cr.get(), checkerColorLightGrey);
    cairo_rectangle(cr.get(), 0, 0, halfCheckerSize, halfCheckerSize);
    cairo_rectangle(cr.get(), halfCheckerSize, halfCheckerSize, halfCheckerSize, halfCheckerSize);
    cairo_fill(cr.get());

    setSourceRGBAFromColor(cr.get(), checkerColorDarkGrey);
    cairo_rectangle(cr.get(), halfCheckerSize, 0, halfCheckerSize, halfCheckerSize);
    cairo_rectangle(cr.get(), 0, halfCheckerSize, halfCheckerSize, halfCheckerSize);
    cairo_fill(cr.get());

    return surface;
}
Exemplo n.º 11
0
static void drawLineOnCairoContext(GraphicsContext* graphicsContext, cairo_t* context, const FloatPoint& point1, const FloatPoint& point2)
{
    StrokeStyle style = graphicsContext->strokeStyle();
    if (style == NoStroke)
        return;

    const Color& strokeColor = graphicsContext->strokeColor();
    int strokeThickness = floorf(graphicsContext->strokeThickness());
    if (graphicsContext->strokeThickness() < 1)
        strokeThickness = 1;

    int patternWidth = 0;
    if (style == DottedStroke)
        patternWidth = strokeThickness;
    else if (style == DashedStroke)
        patternWidth = 3 * strokeThickness;

    bool isVerticalLine = point1.x() == point2.x();
    FloatPoint point1OnPixelBoundaries = point1;
    FloatPoint point2OnPixelBoundaries = point2;
    GraphicsContext::adjustLineToPixelBoundaries(point1OnPixelBoundaries, point2OnPixelBoundaries, strokeThickness, style);

    cairo_set_antialias(context, CAIRO_ANTIALIAS_NONE);
    if (patternWidth) {
        // Do a rect fill of our endpoints.  This ensures we always have the
        // appearance of being a border.  We then draw the actual dotted/dashed line.
        FloatRect firstRect(point1OnPixelBoundaries, FloatSize(strokeThickness, strokeThickness));
        FloatRect secondRect(point2OnPixelBoundaries, FloatSize(strokeThickness, strokeThickness));
        if (isVerticalLine) {
            firstRect.move(-strokeThickness / 2, -strokeThickness);
            secondRect.move(-strokeThickness / 2, 0);
        } else {
            firstRect.move(-strokeThickness, -strokeThickness / 2);
            secondRect.move(0, -strokeThickness / 2);
        }
        fillRectWithColor(context, firstRect, strokeColor);
        fillRectWithColor(context, secondRect, strokeColor);

        int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2 * strokeThickness;
        double patternOffset = calculateStrokePatternOffset(distance, patternWidth);
        double patternWidthAsDouble = patternWidth;
        cairo_set_dash(context, &patternWidthAsDouble, 1, patternOffset);
    }

    setSourceRGBAFromColor(context, strokeColor);
    cairo_set_line_width(context, strokeThickness);
    cairo_move_to(context, point1OnPixelBoundaries.x(), point1OnPixelBoundaries.y());
    cairo_line_to(context, point2OnPixelBoundaries.x(), point2OnPixelBoundaries.y());
    cairo_stroke(context);
}
Exemplo n.º 12
0
void GraphicsContext::platformFillRoundedRect(const FloatRoundedRect& rect, const Color& color, ColorSpace)
{
    if (paintingDisabled())
        return;

    if (hasShadow())
        platformContext()->shadowBlur().drawRectShadow(this, rect);

    cairo_t* cr = platformContext()->cr();
    cairo_save(cr);
    Path path;
    path.addRoundedRect(rect);
    appendWebCorePathToCairoContext(cr, path);
    setSourceRGBAFromColor(cr, color);
    cairo_fill(cr);
    cairo_restore(cr);
}
Exemplo n.º 13
0
void GraphicsContext::drawFocusRing(const Path& path, int width, int /* offset */, const Color& color)
{
    // FIXME: We should draw paths that describe a rectangle with rounded corners
    // so as to be consistent with how we draw rectangular focus rings.
    Color ringColor = color;
    adjustFocusRingColor(ringColor);
    adjustFocusRingLineWidth(width);

    cairo_t* cr = platformContext()->cr();
    cairo_save(cr);
    appendWebCorePathToCairoContext(cr, path);
    setSourceRGBAFromColor(cr, ringColor);
    cairo_set_line_width(cr, width);
    setPlatformStrokeStyle(focusRingStrokeStyle());
    cairo_stroke(cr);
    cairo_restore(cr);
}
Exemplo n.º 14
0
void GraphicsContext::fillRoundedRect(const IntRect& r, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace)
{
    if (paintingDisabled())
        return;

    if (hasShadow())
        platformContext()->shadowBlur().drawRectShadow(this, r, RoundedRect::Radii(topLeft, topRight, bottomLeft, bottomRight));

    cairo_t* cr = platformContext()->cr();
    cairo_save(cr);
    Path path;
    path.addRoundedRect(r, topLeft, topRight, bottomLeft, bottomRight);
    appendWebCorePathToCairoContext(cr, path);
    setSourceRGBAFromColor(cr, color);
    cairo_fill(cr);
    cairo_restore(cr);
}
Exemplo n.º 15
0
static void drawGlyphsShadow(GraphicsContext* graphicsContext, cairo_t* context, const FloatPoint& point, PangoLayoutLine* layoutLine, PangoRegionType renderRegion)
{
    ContextShadow* shadow = graphicsContext->contextShadow();
    ASSERT(shadow);

    if (!(graphicsContext->textDrawingMode() & TextModeFill) || shadow->m_type == ContextShadow::NoShadow)
        return;

    FloatPoint totalOffset(point + shadow->m_offset);

    // Optimize non-blurry shadows, by just drawing text without the ContextShadow.
    if (!shadow->mustUseContextShadow(context)) {
        cairo_save(context);
        cairo_translate(context, totalOffset.x(), totalOffset.y());

        setSourceRGBAFromColor(context, shadow->m_color);
        gdk_cairo_region(context, renderRegion);
        cairo_clip(context);
        pango_cairo_show_layout_line(context, layoutLine);

        cairo_restore(context);
        return;
    }

    FloatRect extents(getPangoRegionExtents(renderRegion));
    extents.setLocation(FloatPoint(point.x(), point.y() - extents.height()));
    cairo_t* shadowContext = shadow->beginShadowLayer(context, extents);
    if (shadowContext) {
        cairo_translate(shadowContext, point.x(), point.y());
        pango_cairo_show_layout_line(shadowContext, layoutLine);

        // We need the clipping region to be active when we blit the blurred shadow back,
        // because we don't want any bits and pieces of characters out of range to be
        // drawn. Since ContextShadow expects a consistent transform, we have to undo the
        // translation before calling endShadowLayer as well.
        cairo_save(context);
        cairo_translate(context, totalOffset.x(), totalOffset.y());
        gdk_cairo_region(context, renderRegion);
        cairo_clip(context);
        cairo_translate(context, -totalOffset.x(), -totalOffset.y());

        shadow->endShadowLayer(context);
        cairo_restore(context);
    }
}
Exemplo n.º 16
0
void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int /* offset */, const Color& color)
{
    if (paintingDisabled())
        return;

    cairo_t* cr = platformContext()->cr();
    cairo_save(cr);
    cairo_push_group(cr);
    cairo_new_path(cr);

#if PLATFORM(GTK)
    for (const auto& rect : rects)
        cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
#else
    unsigned rectCount = rects.size();
    int radius = (width - 1) / 2;
    Path path;
    for (unsigned i = 0; i < rectCount; ++i) {
        if (i > 0)
            path.clear();
        path.addRoundedRect(rects[i], FloatSize(radius, radius));
        appendWebCorePathToCairoContext(cr, path);
    }
#endif
    Color ringColor = color;
    adjustFocusRingColor(ringColor);
    adjustFocusRingLineWidth(width);
    setSourceRGBAFromColor(cr, ringColor);
    cairo_set_line_width(cr, width);
    setPlatformStrokeStyle(focusRingStrokeStyle());

    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
    cairo_stroke_preserve(cr);

    cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
    cairo_fill(cr);

    cairo_pop_group_to_source(cr);
    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
    cairo_paint(cr);
    cairo_restore(cr);
}
Exemplo n.º 17
0
// Draws a filled rectangle with a stroked border.
void GraphicsContext::drawRect(const IntRect& rect)
{
    if (paintingDisabled())
        return;

    cairo_t* cr = platformContext()->cr();
    cairo_save(cr);

    fillRectWithColor(cr, rect, fillColor());

    if (strokeStyle() != NoStroke) {
        setSourceRGBAFromColor(cr, strokeColor());
        FloatRect r(rect);
        r.inflate(-.5f);
        cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height());
        cairo_set_line_width(cr, 1.0);
        cairo_stroke(cr);
    }

    cairo_restore(cr);
}
Exemplo n.º 18
0
void RedirectedXCompositeWindow::createNewPixampAndPixampSurface()
{
    // This should never be called with an empty size (not in Accelerated Compositing mode).
    ASSERT(!m_size.isEmpty());
    XUniquePixmap newPixmap(XCompositeNameWindowPixmap(m_display, m_window.get()));
    if (!newPixmap) {
        cleanupPixmapAndPixmapSurface();
        return;
    }

    XWindowAttributes windowAttributes;
    if (!XGetWindowAttributes(m_display, m_window.get(), &windowAttributes)) {
        cleanupPixmapAndPixmapSurface();
        return;
    }

    RefPtr<cairo_surface_t> newSurface = adoptRef(cairo_xlib_surface_create(m_display, newPixmap.get(), windowAttributes.visual, m_size.width(), m_size.height()));
    cairoSurfaceSetDeviceScale(newSurface.get(), m_webPage.deviceScaleFactor(), m_webPage.deviceScaleFactor());

    RefPtr<cairo_t> cr = adoptRef(cairo_create(newSurface.get()));
    if (!m_webPage.drawsBackground())
        cairo_set_operator(cr.get(), CAIRO_OPERATOR_CLEAR);
    else
        setSourceRGBAFromColor(cr.get(), m_webPage.backgroundColor());
    cairo_paint(cr.get());

    // Nvidia drivers seem to prepare their redirected window pixmap asynchronously, so for a few fractions
    // of a second after each resize, while doing continuous resizing (which constantly destroys and creates
    // pixmap window-backings), the pixmap memory is uninitialized. To work around this issue, paint the old
    // pixmap to the new one to properly initialize it.
    if (m_surface) {
        cairo_set_operator(cr.get(), CAIRO_OPERATOR_OVER);
        cairo_set_source_surface(cr.get(), m_surface.get(), 0, 0);
        cairo_paint(cr.get());
    }

    cleanupPixmapAndPixmapSurface();
    m_pixmap = WTFMove(newPixmap);
    m_surface = WTFMove(newSurface);
}
Exemplo n.º 19
0
void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int /* offset */, const Color& color)
{
    if (paintingDisabled())
        return;

    unsigned rectCount = rects.size();

    cairo_t* cr = platformContext()->cr();
    cairo_save(cr);
    cairo_push_group(cr);
    cairo_new_path(cr);

#if PLATFORM(GTK)
#ifdef GTK_API_VERSION_2
    GdkRegion* reg = gdk_region_new();
#else
    cairo_region_t* reg = cairo_region_create();
#endif

    for (unsigned i = 0; i < rectCount; i++) {
#ifdef GTK_API_VERSION_2
        GdkRectangle rect = rects[i];
        gdk_region_union_with_rect(reg, &rect);
#else
        cairo_rectangle_int_t rect = rects[i];
        cairo_region_union_rectangle(reg, &rect);
#endif
    }
    gdk_cairo_region(cr, reg);
#ifdef GTK_API_VERSION_2
    gdk_region_destroy(reg);
#else
    cairo_region_destroy(reg);
#endif
#else
    int radius = (width - 1) / 2;
    Path path;
    for (unsigned i = 0; i < rectCount; ++i) {
        if (i > 0)
            path.clear();
        path.addRoundedRect(rects[i], FloatSize(radius, radius));
        appendWebCorePathToCairoContext(cr, path);
    }
#endif
    Color ringColor = color;
    adjustFocusRingColor(ringColor);
    adjustFocusRingLineWidth(width);
    setSourceRGBAFromColor(cr, ringColor);
    cairo_set_line_width(cr, width);
    setPlatformStrokeStyle(focusRingStrokeStyle());

    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
    cairo_stroke_preserve(cr);

    cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
    cairo_fill(cr);

    cairo_pop_group_to_source(cr);
    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
    cairo_paint(cr);
    cairo_restore(cr);
}