PyCairo 中的透明度
在 PyCairo 教程的這個部分,我們將討論透明度。我們將提供一些基本的定義和三個有趣的透明度的例子。
透明度是指透過一種材料能夠看到的品質(zhì)。理解透明度最簡單的方法是想象一塊玻璃或水。技術(shù)上來說,光線可以穿過玻璃,因而我們可以看到玻璃后面的物體。
在計(jì)算機(jī)圖形學(xué)中,我們可以用 alpha 合成 實(shí)現(xiàn)透明度效果。Alpha 合成是一個將一幅圖片和背景結(jié)合起來創(chuàng)建部分透明的外觀的過程。合成過程使用 alpha 通道。在用于表達(dá)半透明(透明度)的圖像文件格式中,alpha 通道是一個 8-bit 的layer。每像素額外的 8 bits 被用作一個 mask 并代表256級的半透明度。
透明的矩形
第一個例子將繪制 10 個具有不同半透明度的矩形。
def on_draw(self, wid, cr):for i in range(1, 11):cr.set_source_rgba(0, 0, 1, i * 0.1)cr.rectangle(50 * i, 20, 40, 40)cr.fill()set_source_rgba() 方法有一個 alpha 參數(shù)來提供透明度。
for i in range(1, 11):cr.set_source_rgba(0, 0, 1, i * 0.1)cr.rectangle(50 * i, 20, 40, 40)cr.fill()這段代碼創(chuàng)建了 10 個矩形,它們的 alpha 值分別為 0.1,…,1。
Puff 效果
在下面的例子中,我們創(chuàng)建一個 puff 效果。例子將顯示一個增長的居中的文字,并從一些點(diǎn)開始逐漸褪色。這是一個非常常見的效果,我們經(jīng)常可以在閃光動畫中看到它。paint_with_alpha() 方法對于創(chuàng)建這個效果非常重要。
#!/usr/bin/python''' ZetCode PyCairo tutorialThis program creates a 'puff' effect.author: Jan Bodnar website: zetcode.com last edited: August 2012 '''import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GLib import cairoclass cv(object):SPEED = 14TEXT_SIZE_MAX = 20ALPHA_DECREASE = 0.01SIZE_INCREASE = 0.8class Example(Gtk.Window):def __init__(self):super(Example, self).__init__()self.init_ui()def init_ui(self):self.darea = Gtk.DrawingArea()self.darea.connect("draw", self.on_draw)self.add(self.darea)self.timer = Trueself.alpha = 1.0self.size = 1.0GLib.timeout_add(cv.SPEED, self.on_timer)self.set_title("Puff")self.resize(350, 200)self.set_position(Gtk.WindowPosition.CENTER)self.connect("delete-event", Gtk.main_quit)self.show_all()def on_timer(self):if not self.timer: return Falseself.darea.queue_draw()return Truedef on_draw(self, wid, cr):w, h = self.get_size()cr.set_source_rgb(0.5, 0, 0)cr.paint()cr.select_font_face("Courier", cairo.FONT_SLANT_NORMAL,cairo.FONT_WEIGHT_BOLD)self.size = self.size + cv.SIZE_INCREASEif self.size > cv.TEXT_SIZE_MAX:self.alpha = self.alpha - cv.ALPHA_DECREASEcr.set_font_size(self.size)cr.set_source_rgb(1, 1, 1)(x, y, width, height, dx, dy) = cr.text_extents("ZetCode")cr.move_to(w / 2 - width / 2, h / 2)cr.text_path("ZetCode")cr.clip()cr.paint_with_alpha(self.alpha)if self.alpha <= 0:self.timer = Falsedef main():app = Example()Gtk.main()if __name__ == "__main__":main()這個例子在窗口中創(chuàng)建一段不斷增長并褪色的文本。
class cv(object):SPEED = 14TEXT_SIZE_MAX = 20ALPHA_DECREASE = 0.01SIZE_INCREASE = 0.8這里我們定義了一些將在例子中使用的常量。
self.alpha = 1.0self.size = 1.0這兩個變量存儲當(dāng)前的 alpha 值和字體大小。
GLib.timeout_add(cv.SPEED, self.on_timer)每隔 14 ms,on_timer() 方法被調(diào)用一次。
def on_timer(self):if not self.timer: return Falseself.darea.queue_draw()return True在 on_timer() 方法中,我們通過 queue_draw() 方法重繪 DrawingArea widget。
def on_draw(self, wid, cr):w, h = self.get_size()cr.set_source_rgb(0.5, 0, 0)cr.paint()cr.select_font_face("Courier", cairo.FONT_SLANT_NORMAL,cairo.FONT_WEIGHT_BOLD). . .在 on_draw() 方法中,我們獲取窗口的客戶區(qū)域的寬度和高度。這些值被用于使文字居中。我們將以某種暗紅色填充窗口的背景。我們?yōu)槲淖诌x擇一個 Courier 字體。
(x, y, width, height, dx, dy) = cr.text_extents("ZetCode")我們獲取文字的一些度量值。我們將只使用文字寬度。
cr.move_to(w / 2 - width / 2, h / 2)我們移動到一個可以使文字在窗口中居中的位置。
cr.text_path("ZetCode")cr.clip()cr.paint_with_alpha(self.alpha)我們用 text_path() 方法獲取文字的 path。我們用 clip() 方法將繪制限定在當(dāng)前的path。paint_with_alpha() 方法使用一個 alpha 值的 mask,在當(dāng)前的裁剪區(qū)域內(nèi),繪制當(dāng)前的 source。
圖像倒影
在下一個例子中,我么將秀出一個倒影圖像。這個效果創(chuàng)造了一種圖像倒映在水中的感覺。
#!/usr/bin/python''' ZetCode PyCairo tutorialThis program creates an image reflection.author: Jan Bodnar website: zetcode.com last edited: August 2012 '''import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk import cairo import sysclass Example(Gtk.Window):def __init__(self):super(Example, self).__init__()self.init_ui()self.load_image()self.init_vars()def init_ui(self):darea = Gtk.DrawingArea()darea.connect("draw", self.on_draw)self.add(darea)self.set_title("Reflection")self.resize(300, 350)self.set_position(Gtk.WindowPosition.CENTER)self.connect("delete-event", Gtk.main_quit)self.show_all()def load_image(self):try:self.s = cairo.ImageSurface.create_from_png("slanec.png")except Exception as e:print(e)sys.exit(1)def init_vars(self):self.imageWidth = self.s.get_width()self.imageHeight = self.s.get_height()self.gap = 40self.border = 20def on_draw(self, wid, cr):w, h = self.get_size()lg = cairo.LinearGradient(w / 2, 0, w / 2, h * 3)lg.add_color_stop_rgba(0, 0, 0, 0, 1)lg.add_color_stop_rgba(h, 0.2, 0.2, 0.2, 1)cr.set_source(lg)cr.paint()cr.set_source_surface(self.s, self.border, self.border)cr.paint()alpha = 0.7step = 1.0 / self.imageHeightcr.translate(0, 2 * self.imageHeight + self.gap)cr.scale(1, -1)i = 0while (i < self.imageHeight):cr.rectangle(self.border, self.imageHeight - i,self.imageWidth, 1)i = i + 1cr.save()cr.clip()cr.set_source_surface(self.s, self.border,self.border)alpha = alpha - stepcr.paint_with_alpha(alpha)cr.restore()def main():app = Example()Gtk.main()if __name__ == "__main__":main()在語法方面,需要注意一下 Python 2.7 和 Python 3 在異常處理方面的差異。一個倒映的城堡的廢墟就顯示在窗口中了。
def load_image(self):try:self.s = cairo.ImageSurface.create_from_png("slanec.png")except Exception as e:print(e)sys.exit(1)在 load_image() 方法中,由一幅 PNG 圖片創(chuàng)建一個圖像 surface。
def init_vars(self):self.imageWidth = self.s.get_width()self.imageHeight = self.s.get_height()self.gap = 40self.border = 20在 init_vars() 方法中,我們獲取圖像的寬度和高度。同時(shí)定義兩個變量。
lg = cairo.LinearGradient(w / 2, 0, w / 2, h * 3)lg.add_color_stop_rgba(0, 0, 0, 0, 1)lg.add_color_stop_rgba(h, 0.2, 0.2, 0.2, 1)cr.set_source(lg)cr.paint()窗口的背景由一個漸變繪制填充。繪制是一個平滑的由黑色到深灰色的混合。
cr.translate(0, 2 * self.imageHeight + self.gap)cr.scale(1, -1)這段代碼翻轉(zhuǎn)圖像,并將它平移到原始圖像的下方。平移操作是必須的,因?yàn)榉趴s操作使得圖像翻轉(zhuǎn)并將圖像向上平移。為了理解發(fā)生了什么,可以簡單地拍一張照片,將它放在桌子上,然后反轉(zhuǎn)它。
i = 0while (i < self.imageHeight):cr.rectangle(self.border, self.imageHeight - i,self.imageWidth, 1)i = i + 1cr.save()cr.clip()cr.set_source_surface(self.s, self.border,self.border)alpha = alpha - stepcr.paint_with_alpha(alpha)cr.restore()這是最后的部分。我們使第二幅圖片變得透明。但透明度不是固定的。這幅圖片漸漸地褪色。倒影圖像是一行接一行繪制的。clip() 方法將繪制限定在高度為 1 的矩形中。paint_with_alpha() 在繪制圖像 surface 的當(dāng)前裁剪區(qū)域時(shí)會將透明度也考慮進(jìn)來。
等待效果 Demo
在這個例子中,我們使用透明效果創(chuàng)建一個等待效果的 demo。我們將繪制 8 跳線,它們逐漸的淡出,以創(chuàng)造一種假象,好像線在移動一般。這種效果經(jīng)常被用于告知用戶,一個耗時(shí)比較久的任務(wù)正在后臺運(yùn)行。一個例子是 Interne 上的流視頻。
#!/usr/bin/python''' ZetCode PyCairo tutorialThis program creates a 'waiting' effect.author: Jan Bodnar website: zetcode.com last edited: August 2012 '''import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GLib import cairo import mathclass cv(object):trs = ((0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0),(1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9),(0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8),(0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65),(0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5),(0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3),(0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15),(0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0,))SPEED = 100CLIMIT = 1000NLINES = 8class Example(Gtk.Window):def __init__(self):super(Example, self).__init__()self.init_ui()def init_ui(self):self.darea = Gtk.DrawingArea()self.darea.connect("draw", self.on_draw)self.add(self.darea)self.count = 0GLib.timeout_add(cv.SPEED, self.on_timer)self.set_title("Waiting")self.resize(250, 150)self.set_position(Gtk.WindowPosition.CENTER)self.connect("delete-event", Gtk.main_quit)self.show_all()def on_timer(self):self.count = self.count + 1if self.count >= cv.CLIMIT:self.count = 0self.darea.queue_draw()return Truedef on_draw(self, wid, cr):cr.set_line_width(3)cr.set_line_cap(cairo.LINE_CAP_ROUND)w, h = self.get_size()cr.translate(w / 2, h / 2)for i in range(cv.NLINES):cr.set_source_rgba(0, 0, 0, cv.trs[self.count % 8][i])cr.move_to(0.0, -10.0)cr.line_to(0.0, -40.0)cr.rotate(math.pi / 4)cr.stroke()def main():app = Example()Gtk.main()if __name__ == "__main__":main()我們以八個不同的 alpha 值繪制了八條直線。
class cv(object):trs = ((0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0),(1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9),(0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8),(0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65),(0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5),(0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3),(0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15),(0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0,)) . . .這是一個 Demo 中會用到的透明度值的二維元組。有 8 行,每一行表示一種狀態(tài)。8 條直線中的每一條將連續(xù)使用這些值。
SPEED = 100CLIMIT = 1000NLINES = 8SPEED 常量控制動畫的速度。CLIMIT 是 self.count 變量的最大值。達(dá)到這個限制之后,變量會被重置為 0。NLINES 是這個例子中繪制的直線的條數(shù)。
GLib.timeout_add(cv.SPEED, self.on_timer)我們使用定時(shí)器函數(shù)創(chuàng)建動畫。每一個 cv.SPEED ms,on_timer() 方法會被調(diào)用一次。
def on_timer(self):self.count = self.count + 1if self.count >= cv.CLIMIT:self.count = 0self.darea.queue_draw()return True在 on_timer() 方法中,我們增加 self.count 變量。如果變量達(dá)到了 cv.CLIMIT
常量,它會被設(shè)置為 0。我們避免溢出,并且不使用非常大的數(shù)字。
我們使線更粗一點(diǎn),以使得它們更容易被看到。我們用圓形的帽子繪制直線。
w, h = self.get_size()cr.translate(w / 2, h / 2)我們將我們的繪制定位在窗口的中心。
for i in range(cv.NLINES):cr.set_source_rgba(0, 0, 0, cv.trs[self.count % 8][i])cr.move_to(0.0, -10.0)cr.line_to(0.0, -40.0)cr.rotate(math.pi / 4)cr.stroke()在 for 循環(huán)中,我們用不同的透明度值,繪制了八條旋轉(zhuǎn)的直線。線之間由 45 度角隔開。
本章我們討論了透明度。
原文
Done.
超強(qiáng)干貨來襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生總結(jié)
以上是生活随笔為你收集整理的PyCairo 中的透明度的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PyCairo 中的剪裁和屏蔽
- 下一篇: PyCairo 中的变换