Cairo 示例代码片段及输出

Cairo 示例代码片段及输出#

基于 lazka、stuaxo、fomightez 和 kolibril13 的示例

import cairo
from IPython.display import Image, display
from math import pi
from io import BytesIO

def disp(draw_func):
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 200, 200)
    ctx = cairo.Context(surface)
    draw_func(ctx, 200, 200)
    with BytesIO() as fileobj:
        surface.write_to_png(fileobj)
        display(Image(fileobj.getvalue(), width=200))
@disp
def draw(cr, width, height):
    cr.scale(width, height)
    cr.set_line_width(0.04)

    cr.arc(0.5, 0.5, 0.3, 0, 2 * pi)
    cr.clip()

    cr.rectangle(0, 0, 1, 1)
    cr.fill()
    cr.set_source_rgb(0, 1, 0)
    cr.move_to(0, 0)
    cr.line_to(1, 1)
    cr.move_to(1, 0)
    cr.line_to(0, 1)
    cr.stroke()
@disp
def draw(cr, width, height):
    cr.scale(width, height) # eg1
    cr.stroke()
    cr.set_line_cap(cairo.LINE_CAP_ROUND)
    cr.set_line_width(0.12)
    cr.move_to(0.5, 0.5)
    cr.line_to(0.5, 0.5)
    
    cr.stroke() #eg1
    xc=.4
    yc=.4
    cr.arc(xc, yc, 0.05, 0, 2 * pi)
    cr.fill()
@disp
def draw(cr, width, height):
    cr.scale(width, height)
    cr.set_line_width(0.04)

    # a custom shape, that could be wrapped in a function
    x0 = 0.1  # parameters like cairo_rectangle
    y0 = 0.1
    rect_width = 0.8
    rect_height = 0.8
    radius = 0.4  # and an approximate curvature radius

    x1 = x0 + rect_width
    y1 = y0 + rect_height

    if rect_width / 2 < radius:
        if rect_height / 2 < radius:
            cr.move_to(x0, (y0 + y1) / 2)
            cr.curve_to(x0, y0, x0, y0, (x0 + x1) / 2, y0)
            cr.curve_to(x1, y0, x1, y0, x1, (y0 + y1) / 2)
            cr.curve_to(x1, y1, x1, y1, (x1 + x0) / 2, y1)
            cr.curve_to(x0, y1, x0, y1, x0, (y0 + y1) / 2)
        else:
            cr.move_to(x0, y0 + radius)
            cr.curve_to(x0, y0, x0, y0, (x0 + x1) / 2, y0)
            cr.curve_to(x1, y0, x1, y0, x1, y0 + radius)
            cr.line_to(x1, y1 - radius)
            cr.curve_to(x1, y1, x1, y1, (x1 + x0) / 2, y1)
            cr.curve_to(x0, y1, x0, y1, x0, y1 - radius)
    else:
        if rect_height / 2 < radius:
            cr.move_to(x0, (y0 + y1) / 2)
            cr.curve_to(x0, y0, x0, y0, x0 + radius, y0)
            cr.line_to(x1 - radius, y0)
            cr.curve_to(x1, y0, x1, y0, x1, (y0 + y1) / 2)
            cr.curve_to(x1, y1, x1, y1, x1 - radius, y1)
            cr.line_to(x0 + radius, y1)
            cr.curve_to(x0, y1, x0, y1, x0, (y0 + y1) / 2)
        else:
            cr.move_to(x0, y0 + radius)
            cr.curve_to(x0, y0, x0, y0, x0 + radius, y0)
            cr.line_to(x1 - radius, y0)
            cr.curve_to(x1, y0, x1, y0, x1, y0 + radius)
            cr.line_to(x1, y1 - radius)
            cr.curve_to(x1, y1, x1, y1, x1 - radius, y1)
            cr.line_to(x0 + radius, y1)
            cr.curve_to(x0, y1, x0, y1, x0, y1 - radius)

    cr.close_path()

    cr.set_source_rgb(0.5, 0.5, 1)
    cr.fill_preserve()
    cr.set_source_rgba(0.5, 0, 0, 0.5)
    cr.stroke()
@disp
def draw(cr, width, height):
    cr.scale(width, height)
    cr.set_line_width(0.04)

    x, y = 0.1, 0.5
    x1, y1 = 0.4, 0.9
    x2, y2 = 0.6, 0.1
    x3, y3 = 0.9, 0.5

    cr.move_to(x, y)
    cr.curve_to(x1, y1, x2, y2, x3, y3)

    cr.stroke()

    cr.set_source_rgba(1, 0.2, 0.2, 0.6)
    cr.set_line_width(0.03)
    cr.move_to(x, y)
    cr.line_to(x1, y1)
    cr.move_to(x2, y2)
    cr.line_to(x3, y3)
    cr.stroke()
def path_ellipse(cr, x, y, width, height, angle=0):
    """
    x      - center x
    y      - center y
    width  - width of ellipse  (in x direction when angle=0)
    height - height of ellipse (in y direction when angle=0)
    angle  - angle in radians to rotate, clockwise
    """
    cr.save()
    cr.translate(x, y)
    cr.rotate(angle)
    cr.scale(width / 2.0, height / 2.0)
    cr.arc(0.0, 0.0, 1.0, 0.0, 2.0 * pi)
    cr.restore()

@disp
def draw(cr, width, height):
    cr.scale(width, height)
    cr.set_line_width(0.04)

    path_ellipse(cr, 0.5, 0.5, 1.0, 0.3, pi / 4.0)

    # fill
    cr.set_source_rgba(1, 0, 0, 1)
    cr.fill_preserve()

    # stroke
    # reset identity matrix so line_width is a constant
    # width in device-space, not user-space
    cr.save()
    cr.identity_matrix()
    cr.set_source_rgba(0, 0, 0, 1)
    cr.set_line_width(3)
    cr.stroke()
    cr.restore()
@disp
def draw(cr, width, height):
    cr.scale(width, height)
    cr.set_line_width(0.04)

    cr.move_to(0.5, 0.1)
    cr.line_to(0.9, 0.9)
    cr.rel_line_to(-0.4, 0.0)
    cr.curve_to(0.2, 0.9, 0.2, 0.5, 0.5, 0.5)
    cr.close_path()

    cr.set_source_rgb(0, 0, 1)
    cr.fill_preserve()
    cr.set_source_rgb(0, 0, 0)
    cr.stroke()
@disp
def draw(cr, width, height):
    cr.scale(width, height)
    cr.set_line_width(0.04)

    cr.move_to(0.5, 0.1)
    cr.line_to(0.9, 0.9)
    cr.rel_line_to(-0.4, 0.0)
    cr.curve_to(0.2, 0.9, 0.2, 0.5, 0.5, 0.5)
    cr.close_path()

    cr.move_to(0.25, 0.1)
    cr.rel_line_to(0.2, 0.2)
    cr.rel_line_to(-0.2, 0.2)
    cr.rel_line_to(-0.2, -0.2)
    cr.close_path()

    cr.set_source_rgb(0, 0, 1)
    cr.fill_preserve()
    cr.set_source_rgb(0, 0, 0)
    cr.stroke()
@disp
def draw(cr, width, height):
    cr.scale(width, height)
    cr.set_line_width(0.04)

    pat = cairo.LinearGradient(0.0, 0.0, 0.0, 1.0)
    pat.add_color_stop_rgba(1, 0, 0, 0, 1)
    pat.add_color_stop_rgba(0, 1, 1, 1, 1)
    cr.rectangle(0, 0, 1, 1)
    cr.set_source(pat)
    cr.fill()

    pat = cairo.RadialGradient(0.45, 0.4, 0.1,
                               0.4, 0.4, 0.5)
    pat.add_color_stop_rgba(0, 1, 1, 1, 1)
    pat.add_color_stop_rgba(1, 0, 0, 0, 1)
    cr.set_source(pat)
    cr.arc(0.5, 0.5, 0.3, 0, 2 * pi)
    cr.fill()
@disp
def draw(cr, width, height):
    cr.scale(width, height)
    cr.set_line_width(0.04)

    pattern = cairo.LinearGradient(0, 0, 1, 1)
    pattern.add_color_stop_rgb(0, 0, 0.3, 0.8)
    pattern.add_color_stop_rgb(1, 0, 0.8, 0.3)

    mask = cairo.RadialGradient(0.5, 0.5, 0.25, 0.5, 0.5, 0.5)
    mask.add_color_stop_rgba(0, 0, 0, 0, 1)
    mask.add_color_stop_rgba(0.5, 0, 0, 0, 0)

    cr.set_source(pattern)
    cr.mask(mask)
@disp
def draw(cr, width, height):
    cr.scale(width, height)
    cr.set_line_width(0.04)

    cr.rectangle(0.1, 0.1, 0.6, 0.6)
    cr.set_line_width(0.03)
    cr.set_source_rgb(0.8, 0.8, 0.8)
    cr.fill()

    cr.push_group()
    cr.rectangle(0.3, 0.3, 0.6, 0.6)
    cr.set_source(cairo.SolidPattern(1, 0, 0))
    cr.fill_preserve()
    cr.set_source(cairo.SolidPattern(0, 0, 0))
    cr.stroke()
    cr.pop_group_to_source()
    cr.paint_with_alpha(0.5)
@disp
def draw(ctx, width, height):
    LINES = 32
    MAX_THETA = .80 * pi * 2
    THETA_INC = 2.0 * MAX_THETA / (LINES - 1)

    ctx.set_source_rgb(0, 0, 0)
    ctx.set_line_width(2.0)

    ctx.save()

    ctx.translate(width / 2, height / 2)
    ctx.rotate(MAX_THETA)

    for i in range(LINES):
        ctx.move_to(-2 * width, 0)
        ctx.line_to(2 * width, 0)
        ctx.stroke()

        ctx.rotate(- THETA_INC)

    ctx.restore()

    ctx.set_line_width(6)
    ctx.set_source_rgb(1, 0, 0)

    ctx.move_to(width / 4.0, 0)
    ctx.rel_line_to(0, height)
    ctx.stroke()

    ctx.move_to(3 * width / 4.0, 0)
    ctx.rel_line_to(0, height)
    ctx.stroke()
@disp
def draw(cr, width, height):
    cr.scale(width, height)
    cr.set_line_width(0.04)

    cr.move_to(0.5, 0.1)
    cr.line_to(0.9, 0.9)
    cr.rel_line_to(-0.4, 0.0)
    cr.curve_to(0.2, 0.9, 0.2, 0.5, 0.5, 0.5)

    cr.stroke()
@disp
def draw(cr, width, height):
    cr.scale(width, height)
    cr.set_line_width(0.04)

    cr.set_line_width(0.12)
    cr.set_line_cap(cairo.LINE_CAP_BUTT)  # default
    cr.move_to(0.25, 0.2)
    cr.line_to(0.25, 0.8)
    cr.stroke()
    cr.set_line_cap(cairo.LINE_CAP_ROUND)
    cr.move_to(0.5, 0.2)
    cr.line_to(0.5, 0.8)
    cr.stroke()
    cr.set_line_cap(cairo.LINE_CAP_SQUARE)
    cr.move_to(0.75, 0.2)
    cr.line_to(0.75, 0.8)
    cr.stroke()

    # draw helping lines
    cr.set_source_rgb(1, 0.2, 0.2)
    cr.set_line_width(0.01)
    cr.move_to(0.25, 0.2)
    cr.line_to(0.25, 0.8)
    cr.move_to(0.5, 0.2)
    cr.line_to(0.5, 0.8)
    cr.move_to(0.75, 0.2)
    cr.line_to(0.75, 0.8)
    cr.stroke()
@disp
def draw(cr, width, height):
    cr.scale(width, height)
    cr.set_line_width(0.04)

    cr.set_line_width(0.16)
    cr.move_to(0.3, 0.33)
    cr.rel_line_to(0.2, -0.2)
    cr.rel_line_to(0.2, 0.2)
    cr.set_line_join(cairo.LINE_JOIN_MITER)  # default
    cr.stroke()

    cr.move_to(0.3, 0.63)
    cr.rel_line_to(0.2, -0.2)
    cr.rel_line_to(0.2, 0.2)
    cr.set_line_join(cairo.LINE_JOIN_BEVEL)
    cr.stroke()

    cr.move_to(0.3, 0.93)
    cr.rel_line_to(0.2, -0.2)
    cr.rel_line_to(0.2, 0.2)
    cr.set_line_join(cairo.LINE_JOIN_ROUND)
    cr.stroke()
@disp
def draw(cr, width, height):
    cr.scale(width, height)
    cr.set_line_width(0.04)

    cr.select_font_face("Sans", cairo.FONT_SLANT_NORMAL,
                        cairo.FONT_WEIGHT_NORMAL)
    # draw 0.08 glyphs in 0.10 squares, at(0.01, 0.02) from left corner
    cr.set_font_size(0.08)

    glyphs = []
    index = 0
    for y in range(10):
        for x in range(10):
            glyphs.append((index, x / 10.0 + 0.01, y / 10.0 + 0.08))
            index += 1

    cr.show_glyphs(glyphs)
@disp
def draw(cr, width, height):
    cr.scale(width, height)
    cr.set_line_width(0.04)

    cr.select_font_face("Sans", cairo.FONT_SLANT_NORMAL,
                        cairo.FONT_WEIGHT_NORMAL)
    # draw 0.16 glyphs in 0.20 squares, at (0.02, 0.04) from left corner
    cr.set_font_size(0.16)

    glyphs = []
    index = 20
    for y in range(5):
        for x in range(5):
            glyphs.append((index, x / 5.0 + 0.02, y / 5.0 + 0.16))
            index += 1

    cr.glyph_path(glyphs)
    cr.set_source_rgb(0.5, 0.5, 1.0)
    cr.fill_preserve()
    cr.set_source_rgb(0, 0, 0)
    cr.set_line_width(0.005)
    cr.stroke()
@disp
def draw(ctx, width, height):
    wd = .02 * width
    hd = .02 * height

    width -= 2
    height -= 2

    ctx.move_to(width + 1, 1 - hd)
    for i in range(20):
        ctx.rel_line_to(0, height - hd * (2 * i - 1))
        ctx.rel_line_to(-(width - wd * (2 * i)), 0)
        ctx.rel_line_to(0, -(height - hd * (2 * i)))
        ctx.rel_line_to(width - wd * (2 * i + 1), 0)

    ctx.set_source_rgb(0, 0, 1)
    ctx.stroke()
@disp
def draw(cr, width, height):
    cr.scale(width, height)
    cr.set_line_width(0.04)

    cr.select_font_face("Sans", cairo.FONT_SLANT_NORMAL,
                        cairo.FONT_WEIGHT_BOLD)
    cr.set_font_size(0.35)

    cr.move_to(0.04, 0.53)
    cr.show_text("Hello")

    cr.move_to(0.27, 0.65)
    cr.text_path("void")
    cr.set_source_rgb(0.5, 0.5, 1)
    cr.fill_preserve()
    cr.set_source_rgb(0, 0, 0)
    cr.set_line_width(0.01)
    cr.stroke()

    # draw helping lines
    cr.set_source_rgba(1, 0.2, 0.2, 0.6)
    cr.arc(0.04, 0.53, 0.02, 0, 2 * pi)
    cr.arc(0.27, 0.65, 0.02, 0, 2 * pi)
    cr.fill()
@disp
def draw(cr, width, height):
    cr.scale(width, height)
    cr.set_line_width(0.04)

    utf8 = "cairo"

    cr.select_font_face("Sans",
                        cairo.FONT_SLANT_NORMAL,
                        cairo.FONT_WEIGHT_NORMAL)

    cr.set_font_size(0.2)
    x_bearing, y_bearing, width, height, x_advance, y_advance = \
        cr.text_extents(utf8)
    x = 0.5 - (width / 2 + x_bearing)
    y = 0.5 - (height / 2 + y_bearing)

    cr.move_to(x, y)
    cr.show_text(utf8)

    # draw helping lines
    cr.set_source_rgba(1, 0.2, 0.2, 0.6)
    cr.arc(x, y, 0.05, 0, 2 * pi)
    cr.fill()
    cr.move_to(0.5, 0)
    cr.rel_line_to(0, 1)
    cr.move_to(0, 0.5)
    cr.rel_line_to(1, 0)
    cr.stroke()
@disp
def draw(cr, width, height):
    cr.scale(width, height)
    cr.set_line_width(0.04)

    utf8 = "cairo"

    cr.select_font_face("Sans",
                        cairo.FONT_SLANT_NORMAL,
                        cairo.FONT_WEIGHT_NORMAL)

    cr.set_font_size(0.4)
    x_bearing, y_bearing, width, height, x_advance, y_advance = \
        cr.text_extents(utf8)

    x = 0.1
    y = 0.6

    cr.move_to(x, y)
    cr.show_text(utf8)

    # draw helping lines
    cr.set_source_rgba(1, 0.2, 0.2, 0.6)
    cr.arc(x, y, 0.05, 0, 2 * pi)
    cr.fill()
    cr.move_to(x, y)
    cr.rel_line_to(0, -height)
    cr.rel_line_to(width, 0)
    cr.rel_line_to(x_bearing, -y_bearing)
    cr.stroke()
import math
all_width = all_height = text_width = 0

def warpPath(ctx, function):
    first = True

    for type, points in ctx.copy_path():
        if type == cairo.PATH_MOVE_TO:
            if first:
                ctx.new_path()
                first = False
            x, y = function(*points)
            ctx.move_to(x, y)

        elif type == cairo.PATH_LINE_TO:
            x, y = function(*points)
            ctx.line_to(x, y)

        elif type == cairo.PATH_CURVE_TO:
            x1, y1, x2, y2, x3, y3 = points
            x1, y1 = function(x1, y1)
            x2, y2 = function(x2, y2)
            x3, y3 = function(x3, y3)
            ctx.curve_to(x1, y1, x2, y2, x3, y3)

        elif type == cairo.PATH_CLOSE_PATH:
            ctx.close_path()


def spiral(x, y):
    theta0 = -math.pi * 3 / 4
    theta = x / all_width * math.pi * 2 + theta0
    radius = y + 200 - x / 7
    xnew = radius * math.cos(theta)
    ynew = radius * math.sin(-theta)
    return xnew + all_width / 2, ynew + all_height / 2


def curl(x, y):
    xn = x - text_width / 2
    xnew = xn
    ynew = y + xn ** 3 / ((text_width / 2) ** 3) * 70
    return xnew + all_width / 2, ynew + all_height * 2 / 5


@disp
def draw(ctx, width, height):
    global text_width, all_width, all_height

    all_width, all_height = width, height

    solidpattern = ctx.get_source()

    # background
    pat = cairo.LinearGradient(0.0, 0.0, 0, height)
    pat.add_color_stop_rgba(1, 0, 0, 0, 1)
    pat.add_color_stop_rgba(0, 1, 1, 1, 1)

    ctx.rectangle(0, 0, width, height)
    ctx.set_source(pat)
    ctx.fill()

    # foreground
    ctx.set_source(solidpattern)
    ctx.set_source_rgb(1, 1, 1)

    ctx.select_font_face("Sans")
    ctx.set_font_size(80)

    # spiral text
    ctx.new_path()
    ctx.move_to(0, 0)
    ctx.text_path("pycairo - " + "spam " * 5)
    warpPath(ctx, spiral)
    ctx.fill()

    # curly text
    ctx.new_path()
    ctx.move_to(0, 0)
    ctx.set_source_rgb(0.3, 0.3, 0.3)
    text = "I am curly :)"
    ctx.text_path(text)
    text_width = ctx.text_extents(text)[2]
    warpPath(ctx, curl)
    ctx.fill()
from contextlib import contextmanager

@disp
def draw(ctx, width, height):
    cr = ctx
    @contextmanager
    def saved(cr):
        cr.save()
        try:
            yield cr
        finally:
            cr.restore()

    def Tree(angle):
        cr.move_to(0, 0)
        cr.translate(0, -65)
        cr.line_to(0, 0)
        cr.stroke()
        cr.scale(0.72, 0.72)
        if angle > 0.12:
            for a in [-angle, angle]:
                with saved(cr):
                    cr.rotate(a)
                    Tree(angle * 0.75)


    cr.translate(140, 203)
    cr.set_line_width(5)
    Tree(0.32)