Clipping And masking
In this part of the PyCairo tutorial, we will talk about clipping and masking operations.Clipping
Clipping is restricting of drawing to a certain area. This is done for effeciency reasons and to create interesting effects. PyCairo has aclip()
method to set the clipping. #!/usr/bin/pythonIn this example, we will clip an image. A circle is moving on the window area and showing a part of the underlying image. This is as if we looked through a hole.
'''
ZetCode PyCairo tutorial
This program shows how to perform
clipping in PyCairo.
author: Jan Bodnar
website: zetcode.com
last edited: August 2012
'''
from gi.repository import Gtk, GLib
import cairo
import math
import random
class Example(Gtk.Window):
def __init__(self):
super(Example, self).__init__()
self.init_ui()
self.load_image()
self.init_vars()
def init_ui(self):
self.darea = Gtk.DrawingArea()
self.darea.connect("draw", self.on_draw)
self.add(self.darea)
GLib.timeout_add(100, self.on_timer)
self.set_title("Clipping")
self.resize(300, 200)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
self.show_all()
def load_image(self):
self.image = cairo.ImageSurface.create_from_png("beckov.png")
def init_vars(self):
self.pos_x = 128
self.pos_y = 128
self.radius = 40
self.delta = [3, 3]
def on_timer(self):
self.pos_x += self.delta[0]
self.pos_y += self.delta[1]
self.darea.queue_draw()
return True
def on_draw(self, wid, cr):
w, h = self.get_size()
if (self.pos_x < 0 + self.radius):
self.delta[0] = random.randint(5, 9)
elif (self.pos_x > w - self.radius):
self.delta[0] = -random.randint(5, 9)
if (self.pos_y < 0 + self.radius):
self.delta[1] = random.randint(5, 9)
elif (self.pos_y > h - self.radius):
self.delta[1] = -random.randint(5, 9)
cr.set_source_surface(self.image, 1, 1)
cr.arc(self.pos_x, self.pos_y, self.radius, 0, 2*math.pi)
cr.clip()
cr.paint()
def main():
app = Example()
Gtk.main()
if __name__ == "__main__":
main()
def load_image(self):This is the underlying image. Each timer cycle, we will see a portion of this image.
self.image = cairo.ImageSurface.create_from_png("beckov.png")
if (self.pos_x < 0 + self.radius):If the circle hits the left or the right side of the window, the direction of the circle movement changes randomly. Same applies for the top and bottom sides.
self.delta[0] = random.randint(5, 9)
elif (self.pos_x > w - self.radius):
self.delta[0]= -random.randint(5, 9)
cr.arc(self.pos_x, self.pos_y, self.radius, 0, 2*math.pi)This line adds a circular path to the Cairo context.
cr.clip()The
clip()
sets a clipping region. The clipping region is the current path used. The current path was created by the arc()
method call. cr.paint()The
paint()
paints the current source everywhere within the current clip region. Figure: Clipping
Masking
Before the source is applied to the surface, it is filtered first. The mask is used as a filter. The mask determines where the sourse is applied and where not. Opaque parts of the mask allow to copy the source. Transparent parts do not let to copy the source to the surface.#!/usr/bin/pythonIn the example, the mask determines where to paint and where not to paint.
'''
ZetCode PyCairo tutorial
This program demonstrates masking.
author: Jan Bodnar
website: zetcode.com
last edited: August 2012
'''
from gi.repository import Gtk
import cairo
class Example(Gtk.Window):
def __init__(self):
super(Example, self).__init__()
self.init_ui()
self.load_image()
def init_ui(self):
darea = Gtk.DrawingArea()
darea.connect("draw", self.on_draw)
self.add(darea)
self.set_title("Masking")
self.resize(310, 100)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
self.show_all()
def load_image(self):
self.ims = cairo.ImageSurface.create_from_png("omen.png")
def on_draw(self, wid, cr):
cr.mask_surface(self.ims, 0, 0);
cr.fill()
def main():
app = Example()
Gtk.main()
if __name__ == "__main__":
main()
cr.mask_surface(self.ims, 0, 0);We use an image as a mask, thus displaying it on the window.
cr.fill()
Figure: Masking
Blind down effect
In this code example, we will blind down our image. This is similar to what we do with a roller-blind.#!/usr/bin/pythonThe idea behind the blind down effect is quite simple. The image is h pixels high. We draw 0, 1, 2 ... lines of 1px height. Each cycle the portion of the image is 1px higher, until the whole image is visible.
'''
ZetCode PyCairo tutorial
This program creates a blind down
effect using masking operation.
author: Jan Bodnar
website: zetcode.com
last edited: August 2012
'''
from gi.repository import Gtk, GLib
import cairo
import math
class Example(Gtk.Window):
def __init__(self):
super(Example, self).__init__()
self.init_ui()
self.load_image()
self.init_vars()
def init_ui(self):
self.darea = Gtk.DrawingArea()
self.darea.connect("draw", self.on_draw)
self.add(self.darea)
GLib.timeout_add(35, self.on_timer)
self.set_title("Blind down")
self.resize(325, 250)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
self.show_all()
def load_image(self):
self.image = cairo.ImageSurface.create_from_png("beckov.png")
def init_vars(self):
self.timer = True
self.h = 0
self.iw = self.image.get_width()
self.ih = self.image.get_height()
self.ims = cairo.ImageSurface(cairo.FORMAT_ARGB32,
self.iw, self.ih)
def on_timer(self):
if (not self.timer):
return False
self.darea.queue_draw()
return True
def on_draw(self, wid, cr):
ic = cairo.Context(self.ims)
ic.rectangle(0, 0, self.iw, self.h)
ic.fill()
self.h += 1
if (self.h == self.ih):
self.timer = False
cr.set_source_surface(self.image, 10, 10)
cr.mask_surface(self.ims, 10, 10)
def main():
app = Example()
Gtk.main()
if __name__ == "__main__":
main()
def load_image(self):In the
self.image = cairo.ImageSurface.create_from_png("beckov.png")
load_image()
method, we create an image surface from a PNG image. def init_vars(self):In the init_vars() method, we initiate some variables. We initiate the self.timer and the self.h variables. We get the width and height of the loaded image. And we create an empty image surface. It is going to be filled with lines of pixels from the image surface, that we have created earlier.
self.timer = True
self.h = 0
self.iw = self.image.get_width()
self.ih = self.image.get_height()
self.ims = cairo.ImageSurface(cairo.FORMAT_ARGB32,
self.iw, self.ih)
ic = cairo.Context(self.ims)We create a cairo context from the empty image source.
ic.rectangle(0, 0, self.iw, self.h)We draw a rectangle into the initially empty image. The rectangle will be 1px higher each cycle. The image created this way will serve as a mask later.
ic.fill()
self.h += 1The height of the image to show is increased by one unit.
if (self.h == self.ih):We stop the timer method when we draw the whole image on the GTK window.
self.timer = False
cr.set_source_surface(self.image, 10, 10)The image of a castle is set as a source for painting. The
cr.mask_surface(self.ims, 10, 10)
mask_surface()
paints the current source using the alpha channel of surface as a mask. This chapter covered clipping and masking in PyCairo.
0 comments:
Post a Comment