File: cairo-knockout.py

package info (click to toggle)
py3cairo 1.10.0%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 1,308 kB
  • sloc: python: 12,287; ansic: 4,349; makefile: 141; sh: 32
file content (128 lines) | stat: -rwxr-xr-x 3,558 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/usr/bin/env python
"""Based on gtk+/test/testcairo.c
"""

from __future__ import division
import math
import sys

import cairo
import gtk


def oval_path(ctx, xc, yc, xr, yr):
    ctx.save()

    ctx.translate (xc, yc)
    ctx.scale (1.0, yr / xr)
    ctx.move_to (xr, 0.0)
    ctx.arc (0, 0, xr, 0, 2 * math.pi)
    ctx.close_path ()

    ctx.restore()

def fill_checks(ctx, x, y, width, height):
    CHECK_SIZE = 32

    ctx.rectangle (x, y, width, height)
    ctx.set_source_rgb (0.4, 0.4, 0.4)
    ctx.fill ()

    # Only works for CHECK_SIZE a power of 2
    for j in range (x & -CHECK_SIZE, height, CHECK_SIZE):
        for i in range (y & -CHECK_SIZE, width, CHECK_SIZE):
            if ((i / CHECK_SIZE + j / CHECK_SIZE) % 2 == 0):
                ctx.rectangle (i, j, CHECK_SIZE, CHECK_SIZE)

    ctx.set_source_rgb (0.7, 0.7, 0.7)
    ctx.fill ()

def draw_3circles(ctx, xc, yc, radius, alpha):
    subradius = radius * (2 / 3. - 0.1)

    ctx.set_source_rgba(1, 0, 0, alpha)
    oval_path(ctx,
              xc + radius / 3. * math.cos(math.pi * 0.5),
              yc - radius / 3. * math.sin(math.pi * 0.5),
              subradius, subradius)
    ctx.fill()

    ctx.set_source_rgba(0, 1, 0, alpha)
    oval_path(ctx,
              xc + radius / 3. * math.cos(math.pi * (0.5 + 2/.3)),
              yc - radius / 3. * math.sin(math.pi * (0.5 + 2/.3)),
              subradius, subradius)
    ctx.fill()

    ctx.set_source_rgba(0, 0, 1, alpha)
    oval_path(ctx,
              xc + radius / 3. * math.cos(math.pi * (0.5 + 4/.3)),
              yc - radius / 3. * math.sin(math.pi * (0.5 + 4/.3)),
              subradius, subradius)
    ctx.fill()

def draw (ctx, width, height):
    radius = 0.5 * min(width, height) - 10
    xc = width / 2.
    yc = height / 2.

    target  = ctx.get_target()
    overlay = target.create_similar(cairo.CONTENT_COLOR_ALPHA, width, height)
    punch   = target.create_similar(cairo.CONTENT_ALPHA, width, height)
    circles = target.create_similar(cairo.CONTENT_COLOR_ALPHA, width, height)

    fill_checks(ctx, 0, 0, width, height)

    # Draw a black circle on the overlay
    overlay_cr = cairo.Context (overlay)
    overlay_cr.set_source_rgb (0, 0, 0)
    oval_path (overlay_cr, xc, yc, radius, radius)
    overlay_cr.fill()

    # Draw 3 circles to the punch surface, then cut
    # that out of the main circle in the overlay
    punch_cr = cairo.Context (punch)
    draw_3circles (punch_cr, xc, yc, radius, 1.0)

    overlay_cr.set_operator (cairo.OPERATOR_DEST_OUT)
    overlay_cr.set_source_surface (punch, 0, 0)
    overlay_cr.paint()

    # Now draw the 3 circles in a subgroup again
    # at half intensity, and use OperatorAdd to join up
    # without seams.
    circles_cr = cairo.Context (circles)

    circles_cr.set_operator (cairo.OPERATOR_OVER)
    draw_3circles (circles_cr, xc, yc, radius, 0.5)

    overlay_cr.set_operator (cairo.OPERATOR_ADD)
    overlay_cr.set_source_surface (circles, 0, 0)
    overlay_cr.paint()

    ctx.set_source_surface (overlay, 0, 0)
    ctx.paint()

def expose(drawingarea, event):
    ctx = drawingarea.window.cairo_create()

    _, _, width, height = drawingarea.allocation
    draw (ctx, width, height)

    return False

def main():
    win = gtk.Window()
    win.connect('destroy', gtk.main_quit)
    win.set_title('Knockout Groups')
    win.set_default_size(400, 400)

    drawingarea = gtk.DrawingArea()
    win.add(drawingarea)
    drawingarea.connect('expose_event', expose)

    win.show_all()
    gtk.main()

if __name__ == '__main__':
    main()