原文: https://zetcode.com/gfx/pycairo/transparency/

在 PyCairo 教程的这一部分中,我们将讨论透明度。 我们将提供一些基本定义和三个有趣的透明度示例。

透明性是指能够透视材料的质量。 了解透明度的最简单方法是想象一块玻璃或水。 从技术上讲,光线可以穿过玻璃,这样我们就可以看到玻璃后面的物体。

在计算机图形学中,我们可以使用 alpha 合成实现透明效果。 Alpha 合成是将图像与背景组合以创建部分透明外观的过程。 合成过程使用 alpha 通道。 Alpha 通道是图形文件格式的 8 位层,用于表达半透明性(透明度)。 每个像素的额外八位用作掩码,表示 256 级半透明。
(answers.com,wikipedia.org)

透明矩形

第一个示例将绘制十个透明度不同的矩形。

  1. def on_draw(self, wid, cr):
  2. for i in range(1, 11):
  3. cr.set_source_rgba(0, 0, 1, i*0.1)
  4. cr.rectangle(50*i, 20, 40, 40)
  5. cr.fill()

set_source_rgba()方法具有 alpha 参数以提供透明度。

  1. for i in range(1, 11):
  2. cr.set_source_rgba(0, 0, 1, i*0.1)
  3. cr.rectangle(50*i, 20, 40, 40)
  4. cr.fill()

此代码创建十个矩形,其 alpha 值从 0.1 到 1。

PyCairo 的透明度 - 图1

图:透明矩形

泡芙效果

在以下示例中,我们创建一个粉扑效果。 该示例将显示一个不断增长的居中文本,该文本将从某个点逐渐淡出。 这是一个非常常见的效果,我们经常可以在 Flash 动画中看到它。 paint_with_alpha()方法对于产生效果至关重要。

  1. #!/usr/bin/python
  2. '''
  3. ZetCode PyCairo tutorial
  4. This program creates a 'puff'
  5. effect.
  6. author: Jan Bodnar
  7. website: zetcode.com
  8. last edited: August 2012
  9. '''
  10. from gi.repository import Gtk, GLib
  11. import cairo
  12. class cv(object):
  13. SPEED = 14
  14. TEXT_SIZE_MAX = 20
  15. ALPHA_DECREASE = 0.01
  16. SIZE_INCREASE = 0.8
  17. class Example(Gtk.Window):
  18. def __init__(self):
  19. super(Example, self).__init__()
  20. self.init_ui()
  21. def init_ui(self):
  22. self.darea = Gtk.DrawingArea()
  23. self.darea.connect("draw", self.on_draw)
  24. self.add(self.darea)
  25. self.timer = True
  26. self.alpha = 1.0
  27. self.size = 1.0
  28. GLib.timeout_add(cv.SPEED, self.on_timer)
  29. self.set_title("Puff")
  30. self.resize(350, 200)
  31. self.set_position(Gtk.WindowPosition.CENTER)
  32. self.connect("delete-event", Gtk.main_quit)
  33. self.show_all()
  34. def on_timer(self):
  35. if not self.timer: return False
  36. self.darea.queue_draw()
  37. return True
  38. def on_draw(self, wid, cr):
  39. w, h = self.get_size()
  40. cr.set_source_rgb(0.5, 0, 0)
  41. cr.paint()
  42. cr.select_font_face("Courier", cairo.FONT_SLANT_NORMAL,
  43. cairo.FONT_WEIGHT_BOLD)
  44. self.size = self.size + cv.SIZE_INCREASE
  45. if self.size > cv.TEXT_SIZE_MAX:
  46. self.alpha = self.alpha - cv.ALPHA_DECREASE
  47. cr.set_font_size(self.size)
  48. cr.set_source_rgb(1, 1, 1)
  49. (x, y, width, height, dx, dy) = cr.text_extents("ZetCode")
  50. cr.move_to(w/2 - width/2, h/2)
  51. cr.text_path("ZetCode")
  52. cr.clip()
  53. cr.paint_with_alpha(self.alpha)
  54. if self.alpha <= 0:
  55. self.timer = False
  56. def main():
  57. app = Example()
  58. Gtk.main()
  59. if __name__ == "__main__":
  60. main()

该示例在窗口上创建一个逐渐增长和褪色的文本。

  1. class cv(object):
  2. SPEED = 14
  3. TEXT_SIZE_MAX = 20
  4. ALPHA_DECREASE = 0.01
  5. SIZE_INCREASE = 0.8

在这里,我们定义了示例中使用的一些常量。

  1. self.alpha = 1.0
  2. self.size = 1.0

这两个变量存储当前的 alpha 值和文本大小。

  1. GLib.timeout_add(cv.SPEED, self.on_timer)

每 14 毫秒调用一次on_timer()方法。

  1. def on_timer(self):
  2. if not self.timer: return False
  3. self.darea.queue_draw()
  4. return True

on_timer()方法中,我们使用queue_draw()方法重绘绘图区域小部件。

  1. def on_draw(self, wid, cr):
  2. w, h = self.get_size()
  3. cr.set_source_rgb(0.5, 0, 0)
  4. cr.paint()
  5. cr.select_font_face("Courier", cairo.FONT_SLANT_NORMAL,
  6. cairo.FONT_WEIGHT_BOLD)
  7. ...

on_draw()方法中,我们获得窗口工作区的宽度和高度。 这些值用于使文本居中。 我们用一些深红色填充窗口的背景。 我们为文本选择一种 Courier 字体。

  1. (x, y, width, height, dx, dy) = cr.text_extents("ZetCode")

我们得到了文本指标。 我们将仅使用文本宽度。

  1. cr.move_to(w/2 - width/2, h/2)

我们移动到文本将在窗口上居中的位置。

  1. cr.text_path("ZetCode")
  2. cr.clip()
  3. cr.paint_with_alpha(self.alpha)

我们使用text_path()方法获得文本的路径。 我们使用clip()方法将绘画限制为当前路径。 paint_with_alpha()方法使用 alpha 值的掩码在当前剪裁区域内的任何地方绘制当前源。

PyCairo 的透明度 - 图2

图:粉扑效果

反射图像

在下一个示例中,我们显示反射图像。 这种效果使人产生幻觉,好像图像在水中被反射一样。

  1. #!/usr/bin/python
  2. '''
  3. ZetCode PyCairo tutorial
  4. This program creates an image reflection.
  5. author: Jan Bodnar
  6. website: zetcode.com
  7. last edited: August 2012
  8. '''
  9. from gi.repository import Gtk
  10. import cairo
  11. import sys
  12. class Example(Gtk.Window):
  13. def __init__(self):
  14. super(Example, self).__init__()
  15. self.init_ui()
  16. self.load_image()
  17. self.init_vars()
  18. def init_ui(self):
  19. darea = Gtk.DrawingArea()
  20. darea.connect("draw", self.on_draw)
  21. self.add(darea)
  22. self.set_title("Reflection")
  23. self.resize(300, 350)
  24. self.set_position(Gtk.WindowPosition.CENTER)
  25. self.connect("delete-event", Gtk.main_quit)
  26. self.show_all()
  27. def load_image(self):
  28. try:
  29. self.s = cairo.ImageSurface.create_from_png("slanec.png")
  30. except Exception, e:
  31. print e.message
  32. sys.exit(1)
  33. def init_vars(self):
  34. self.imageWidth = self.s.get_width()
  35. self.imageHeight = self.s.get_height()
  36. self.gap = 40
  37. self.border = 20
  38. def on_draw(self, wid, cr):
  39. w, h = self.get_size()
  40. lg = cairo.LinearGradient(w/2, 0, w/2, h*3)
  41. lg.add_color_stop_rgba(0, 0, 0, 0, 1)
  42. lg.add_color_stop_rgba(h, 0.2, 0.2, 0.2, 1)
  43. cr.set_source(lg)
  44. cr.paint()
  45. cr.set_source_surface(self.s, self.border, self.border)
  46. cr.paint()
  47. alpha = 0.7
  48. step = 1.0 / self.imageHeight
  49. cr.translate(0, 2 * self.imageHeight + self.gap)
  50. cr.scale(1, -1)
  51. i = 0
  52. while(i < self.imageHeight):
  53. cr.rectangle(self.border, self.imageHeight-i,
  54. self.imageWidth, 1)
  55. i = i + 1
  56. cr.save()
  57. cr.clip()
  58. cr.set_source_surface(self.s, self.border,
  59. self.border)
  60. alpha = alpha - step
  61. cr.paint_with_alpha(alpha)
  62. cr.restore()
  63. def main():
  64. app = Example()
  65. Gtk.main()
  66. if __name__ == "__main__":
  67. main()

窗户上显示了一座城堡的倒影。

  1. def load_image(self):
  2. try:
  3. self.s = cairo.ImageSurface.create_from_png("slanec.png")
  4. except Exception, e:
  5. print e.message
  6. sys.exit(1)

load_image()方法中,从 PNG 图像创建图像表面。

  1. def init_vars(self):
  2. self.imageWidth = self.s.get_width()
  3. self.imageHeight = self.s.get_height()
  4. self.gap = 40
  5. self.border = 20

init_vars()方法内部,我们获得图像的宽度和高度。 我们还定义了两个变量。

  1. lg = cairo.LinearGradient(w/2, 0, w/2, h*3)
  2. lg.add_color_stop_rgba(0, 0, 0, 0, 1)
  3. lg.add_color_stop_rgba(h, 0.2, 0.2, 0.2, 1)
  4. cr.set_source(lg)
  5. cr.paint()

窗口的背景填充有渐变颜料。 涂料是从黑色到深灰色的平滑混合。

  1. cr.translate(0, 2 * self.imageHeight + self.gap)
  2. cr.scale(1, -1)

此代码翻转图像并将其转换为原始图像下方。 平移操作是必需的,因为缩放操作会使图像上下颠倒并向上平移图像。 要了解发生了什么,只需拍摄一张照片并将其放在桌子上即可。 并翻转它。

  1. i = 0
  2. while(i < self.imageHeight):
  3. cr.rectangle(self.border, self.imageHeight-i,
  4. self.imageWidth, 1)
  5. i = i + 1
  6. cr.save()
  7. cr.clip()
  8. cr.set_source_surface(self.s, self.border,
  9. self.border)
  10. alpha = alpha - step
  11. cr.paint_with_alpha(alpha)
  12. cr.restore()

这是最后一部分。 我们使第二个图像透明。 但是透明度不是恒定的。 图像逐渐淡出。 反射的图像逐行绘制。 clip()方法将图形限制为高度为 1 的矩形。paint_with_alpha()在绘制图像表面的当前剪裁时会考虑透明度。

PyCairo 的透明度 - 图3

图:反射图像

等待演示

在此示例中,我们使用透明效果创建一个等待演示。 我们将绘制 8 条线,这些线将逐渐消失,从而产生一种错觉,即一条线在移动。 此类效果通常用于通知用户幕后正在进行繁重的任务。 一个示例是通过互联网流式传输视频。

  1. #!/usr/bin/python
  2. '''
  3. ZetCode PyCairo tutorial
  4. This program creates a 'waiting' effect.
  5. author: Jan Bodnar
  6. website: zetcode.com
  7. last edited: August 2012
  8. '''
  9. from gi.repository import Gtk, GLib
  10. import cairo
  11. import math
  12. class cv(object):
  13. trs = (
  14. ( 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 ),
  15. ( 1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9 ),
  16. ( 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8 ),
  17. ( 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65 ),
  18. ( 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5 ),
  19. ( 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3 ),
  20. ( 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15 ),
  21. ( 0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, )
  22. )
  23. SPEED = 100
  24. CLIMIT = 1000
  25. NLINES = 8
  26. class Example(Gtk.Window):
  27. def __init__(self):
  28. super(Example, self).__init__()
  29. self.init_ui()
  30. def init_ui(self):
  31. self.darea = Gtk.DrawingArea()
  32. self.darea.connect("draw", self.on_draw)
  33. self.add(self.darea)
  34. self.count = 0
  35. GLib.timeout_add(cv.SPEED, self.on_timer)
  36. self.set_title("Waiting")
  37. self.resize(250, 150)
  38. self.set_position(Gtk.WindowPosition.CENTER)
  39. self.connect("delete-event", Gtk.main_quit)
  40. self.show_all()
  41. def on_timer(self):
  42. self.count = self.count + 1
  43. if self.count >= cv.CLIMIT:
  44. self.count = 0
  45. self.darea.queue_draw()
  46. return True
  47. def on_draw(self, wid, cr):
  48. cr.set_line_width(3)
  49. cr.set_line_cap(cairo.LINE_CAP_ROUND)
  50. w, h = self.get_size()
  51. cr.translate(w/2, h/2)
  52. for i in range(cv.NLINES):
  53. cr.set_source_rgba(0, 0, 0, cv.trs[self.count%8][i])
  54. cr.move_to(0.0, -10.0)
  55. cr.line_to(0.0, -40.0)
  56. cr.rotate(math.pi/4)
  57. cr.stroke()
  58. def main():
  59. app = Example()
  60. Gtk.main()
  61. if __name__ == "__main__":
  62. main()

我们用八个不同的 alpha 值绘制八条线。

  1. class cv(object):
  2. trs = (
  3. ( 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 ),
  4. ( 1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9 ),
  5. ( 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8 ),
  6. ( 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65 ),
  7. ( 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5 ),
  8. ( 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3 ),
  9. ( 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15 ),
  10. ( 0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, )
  11. )
  12. ...

这是此演示中使用的透明度值的二维元组。 有 8 行,每行一种状态。 8 行中的每行将连续使用这些值。

  1. SPEED = 100
  2. CLIMIT = 1000
  3. NLINES = 8

SPEED常数控制动画的速度。 CLIMITself.count变量的最大数量。 达到此限制后,变量将重置为 0。NLINES是示例中绘制的行数。

  1. GLib.timeout_add(cv.SPEED, self.on_timer)

我们使用计时器函数来创建动画。 on_timer()方法的每个cv.SPEED ms 被调用。

  1. def on_timer(self):
  2. self.count = self.count + 1
  3. if self.count >= cv.CLIMIT:
  4. self.count = 0
  5. self.darea.queue_draw()
  6. return True

on_timer()方法中,我们增加self.count变量。 如果变量达到cv.CLIMIT常量,则将其设置为 0。我们防止溢出,并且不使用大数。

  1. def on_draw(self, wid, cr):
  2. cr.set_line_width(3)
  3. cr.set_line_cap(cairo.LINE_CAP_ROUND)
  4. ...

我们使线条更粗一些,以便更好地显示它们。 我们用带帽的线画线。

  1. w, h = self.get_size()
  2. cr.translate(w/2, h/2)

我们将图形放置在窗口的中央。

  1. for i in range(cv.NLINES):
  2. cr.set_source_rgba(0, 0, 0, cv.trs[self.count%8][i])
  3. cr.move_to(0.0, -10.0)
  4. cr.line_to(0.0, -40.0)
  5. cr.rotate(math.pi/4)
  6. cr.stroke()

for循环中,我们绘制了 8 条具有不同透明度值的旋转线。 线以 45 度角分开。

PyCairo 的透明度 - 图4

图:等待 demo

在 PyCairo 教程的这一部分中,我们介绍了透明度。