原文:ZetCode
协议:CC BY-NC-SA 4.0
Cairo 中的图像
原文: https://zetcode/gfx/cairo/cairoimages/
在 Cairo 图形教程的这一部分中,我们将讨论图像。 我们将展示如何在 GTK 窗口上显示图像。 我们还将用图像创建一些效果。
显示图像
在第一个示例中,我们将显示一个图像。
#include <cairo.h>
#include <gtk/gtk.h>
struct {
cairo_surface_t *image;
} glob;
static void do_drawing(cairo_t *);
static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr,
gpointer user_data)
{
do_drawing(cr);
return FALSE;
}
static void do_drawing(cairo_t *cr)
{
cairo_set_source_surface(cr, glob.image, 10, 10);
cairo_paint(cr);
}
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *darea;
glob.image = cairo_image_surface_create_from_png("stmichaelschurch.png");
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
darea = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER (window), darea);
g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(window, "destroy",
G_CALLBACK (gtk_main_quit), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 220);
gtk_window_set_title(GTK_WINDOW(window), "Image");
gtk_widget_show_all(window);
gtk_main();
cairo_surface_destroy(glob.image);
return 0;
}
该示例显示图像。
glob.image = cairo_image_surface_create_from_png("stmichaelschurch.png");
我们从 PNG 图像创建图像表面。 出于效率原因,该函数在主函数中调用。
cairo_set_source_surface(cr, glob.image, 10, 10);
我们从创建的图像表面创建一个绘画源。
cairo_paint(cr);
我们在窗口上绘制源。
cairo_surface_destroy(glob.image);
最后,表面被破坏。
水印
在图像上绘制信息是很常见的。 写在图像上的文本称为水印。 水印用于识别图像。 它们可能是版权声明或图像创建时间。
#include <cairo.h>
#include <gtk/gtk.h>
static void do_drawing(cairo_t *, GtkWidget *widget);
struct {
cairo_surface_t *image;
} glob;
static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr,
gpointer user_data)
{
do_drawing(cr, widget);
return FALSE;
}
static void do_drawing(cairo_t *cr, GtkWidget *widget)
{
cairo_set_source_surface(cr, glob.image, 10, 10);
cairo_paint(cr);
}
static void load_image()
{
glob.image = cairo_image_surface_create_from_png("beckov.png");
}
static void draw_mark()
{
cairo_t *ic;
ic = cairo_create(glob.image);
cairo_set_font_size(ic, 11);
cairo_set_source_rgb(ic, 0.9 , 0.9 , 0.9);
cairo_move_to(ic, 20, 30);
cairo_show_text(ic, " Beckov 2012 , (c) Jan Bodnar ");
cairo_stroke(ic);
}
int main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *darea;
load_image();
draw_mark();
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
darea = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER (window), darea);
g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 350, 250);
gtk_window_set_title(GTK_WINDOW(window), "Watermark");
gtk_widget_show_all(window);
gtk_main();
cairo_surface_destroy(glob.image);
return 0;
}
我们在图像上绘制版权信息。
static void load_image()
{
glob.image = cairo_image_surface_create_from_png("beckov.png");
}
在load_image()
方法中,我们从 PNG 图像创建图像表面。
static void draw_mark()
{
cairo_t *ic;
ic = cairo_create(glob.image);
...
在draw_mark()
函数中,我们在图像上绘制版权信息。 首先,我们从图像表面创建一个绘图上下文。
cairo_set_font_size(ic, 11);
cairo_set_source_rgb(ic, 0.9 , 0.9 , 0.9);
cairo_move_to(ic, 20, 30);
cairo_show_text(ic, " Beckov 2012 , (c) Jan Bodnar ");
cairo_stroke(ic);
然后,我们用白色绘制一个小的文本。
static void do_drawing(cairo_t *cr, GtkWidget *widget)
{
cairo_set_source_surface(cr, glob.image, 10, 10);
cairo_paint(cr);
}
图像表面绘制在窗口上。
频谱效应
我们称其为频谱效应,因为它类似于旧的 ZX 频谱计算机。 当您将图像加载到这台计算机时,它逐渐出现在屏幕上。 下一个例子是基于这种经验。
#include <cairo.h>
#include <gtk/gtk.h>
static void do_drawing(cairo_t *);
struct {
gboolean timer;
cairo_surface_t *image;
cairo_surface_t *surface;
gint img_width;
gint img_height;
} glob;
static void init_vars()
{
glob.image = cairo_image_surface_create_from_png("beckov.png");
glob.img_width = cairo_image_surface_get_width(glob.image);
glob.img_height = cairo_image_surface_get_height(glob.image);
glob.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
glob.img_width, glob.img_height);
glob.timer = TRUE;
}
static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr,
gpointer user_data)
{
do_drawing(cr);
return FALSE;
}
static void do_drawing(cairo_t *cr)
{
cairo_t *ic;
static gint count = 0;
ic = cairo_create(glob.surface);
gint i, j;
for (i = 0; i <= glob.img_height; i+=7) {
for (j = 0 ; j < count; j++) {
cairo_move_to(ic, 0, i+j);
cairo_line_to(ic, glob.img_width, i+j);
}
}
count++;
if (count == 8) glob.timer = FALSE;
cairo_set_source_surface(cr, glob.image, 10, 10);
cairo_mask_surface(cr, glob.surface, 10, 10);
cairo_stroke(ic);
cairo_destroy(ic);
}
static gboolean time_handler(GtkWidget *widget)
{
if (!glob.timer) return FALSE;
gtk_widget_queue_draw(widget);
return TRUE;
}
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *darea;
init_vars();
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
darea = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER (window), darea);
g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 325, 250);
gtk_window_set_title(GTK_WINDOW(window), "Spectrum");
g_timeout_add(400, (GSourceFunc) time_handler, (gpointer) window);
gtk_widget_show_all(window);
gtk_main();
cairo_surface_destroy(glob.image);
cairo_surface_destroy(glob.surface);
return 0;
}
我们将图像分为由 8 行组成的 n 个部分。 每个周期,图像的每个部分都会变大一个像素。 创建的图像将用作显示城堡图像的遮罩。
struct {
gboolean timer;
cairo_surface_t *image;
cairo_surface_t *surface;
gint img_width;
gint img_height;
} glob;
全局结构存储在更多函数中使用的变量。
static void init_vars()
{
glob.image = cairo_image_surface_create_from_png("beckov.png");
glob.img_width = cairo_image_surface_get_width(glob.image);
glob.img_height = cairo_image_surface_get_height(glob.image);
glob.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
glob.img_width, glob.img_height);
glob.timer = TRUE;
}
在init_vars()
函数中,我们启动上述变量。
gint i, j;
for (i = 0; i <= glob.img_height; i+=7) {
for (j = 0 ; j < count; j++) {
cairo_move_to(ic, 0, i+j);
cairo_line_to(ic, glob.img_width, i+j);
}
}
我们逐步将线绘制到 n 个部分中的每个部分。
count++;
if (count == 8) glob.timer = FALSE;
8 个步骤后,动画结束。
cairo_set_source_surface(cr, glob.image, 10, 10);
cairo_mask_surface(cr, glob.surface, 10, 10);
cairo_stroke(ic);
使用遮罩操作,我们在窗口上绘制图像的各个部分。
本章介绍了 Cairo 的图像。
根窗口
原文: https://zetcode/gfx/cairo/root/
在 Cairo 图形教程的这一部分中,我们将使用根窗口。 根窗口是我们通常具有图标快捷方式的桌面窗口。
可以使用根窗口进行操作。 从程序员的角度来看,它只是一种特殊的窗口。
透明窗
我们的第一个示例将创建一个透明窗口。 我们将看到窗口对象下方的内容。
#include <cairo.h>
#include <gtk/gtk.h>
static void do_drawing(cairo_t *);
static void tran_setup(GtkWidget *win)
{
GdkScreen *screen;
GdkVisual *visual;
gtk_widget_set_app_paintable(win, TRUE);
screen = gdk_screen_get_default();
visual = gdk_screen_get_rgba_visual(screen);
if (visual != NULL && gdk_screen_is_composited(screen)) {
gtk_widget_set_visual(win, visual);
}
}
static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr,
gpointer user_data)
{
do_drawing(cr);
return FALSE;
}
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.4);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_paint(cr);
}
int main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *darea;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
tran_setup(window);
darea = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER (window), darea);
g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 250);
gtk_window_set_title(GTK_WINDOW(window), "Transparent window");
gtk_widget_show_all(window);
gtk_main();
return 0;
}
为了创建透明窗口,我们获得了屏幕对象的视觉效果并将其设置为我们的窗口。 在on_draw()
方法中,我们绘制屏幕的可视对象。 这产生了部分透明的幻觉。
gtk_widget_set_app_paintable(win, TRUE);
我们必须设置要绘制的应用。
screen = gdk_screen_get_default();
gdk_screen_get_default()
方法返回屏幕对象。
visual = gdk_screen_get_rgba_visual(screen);
从屏幕窗口中,我们可以看到它。 视觉内容包含低级显示信息。
if (visual != NULL && gdk_screen_is_composited(screen)) {
gtk_widget_set_visual(win, visual);
}
并非所有的显示器都支持此操作。 因此,我们检查屏幕是否支持合成并且返回的视觉效果不是NULL
。 我们将屏幕的视觉效果设置为窗口的视觉效果。
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.4);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_paint(cr);
}
我们使用部分透明的源来绘制屏幕窗口。 CAIRO_OPERATOR_SOURCE
在我们绘制源代码的地方创建了合成操作。 这是屏幕窗口。 为了获得完全透明,我们将 alpha 值设置为 0 或使用CAIRO_OPERATOR_CLEAR
运算符。
图:透明窗口
截屏
根窗口对于截图也是必不可少的。
#include <cairo.h>
#include <gdk/gdk.h>
int main (int argc, char *argv[])
{
gdk_init(&argc, &argv);
GdkWindow *root_win = gdk_get_default_root_window();
gint width = gdk_window_get_width(root_win);
gint height = gdk_window_get_height(root_win);
cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
width, height);
GdkPixbuf *pb = gdk_pixbuf_get_from_window(root_win, 0, 0, width, height);
cairo_t *cr = cairo_create(surface);
gdk_cairo_set_source_pixbuf(cr, pb, 0, 0);
cairo_paint(cr);
cairo_surface_write_to_png(surface, "image.png");
cairo_destroy(cr);
cairo_surface_destroy(surface);
return 0;
}
该示例捕获整个屏幕的快照。 在此示例中,我们不使用完整的 GTK 窗口系统。 我们使用 Cairo 和 GDK 库来完成这项工作。
gdk_init(&argc, &argv);
gdk_init()
初始化 GDK 库并连接到窗口系统。
GdkWindow *root_win = gdk_get_default_root_window();
我们通过gdk_get_default_root_window()
函数调用获得了根窗口。
gint width = gdk_window_get_width(root_win);
gint height = gdk_window_get_height(root_win);
我们确定根窗口的宽度和高度。
cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
width, height);
空的图像表面被创建。 它具有根窗口的大小。
GdkPixbuf *pb = gdk_pixbuf_get_from_window(root_win, 0, 0, width, height);
我们使用gdk_pixbuf_get_from_window()
函数调用从根窗口中获得一个pixbuf
。 pixbuf
是描述内存中图像的对象。
cairo_t *cr = cairo_create(surface);
gdk_cairo_set_source_pixbuf(cr, pb, 0, 0);
cairo_paint(cr);
在上述代码行中,我们在之前创建的图像表面上创建了 Cairo 绘图上下文。 我们将 pixbuf 放在绘图上下文上并将其绘制在表面上。
cairo_surface_write_to_png(surface, "image.png");
使用write_to_png()
方法将图像表面写入 PNG 图像。
cairo_destroy(cr);
cairo_surface_destroy(surface);
我们清理资源。
显示信息
在第三个示例中,我们将在桌面窗口上显示一条消息。
#include <cairo.h>
#include <gtk/gtk.h>
#include <pango/pango.h>
static void do_drawing(cairo_t *);
static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr,
gpointer user_data)
{
do_drawing(cr);
return FALSE;
}
static void do_drawing(cairo_t *cr)
{
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
cairo_paint(cr);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
}
static void setup(GtkWidget *win)
{
gtk_widget_set_app_paintable(win, TRUE);
gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_DOCK);
gtk_window_set_keep_below(GTK_WINDOW(win), TRUE);
GdkScreen *screen = gdk_screen_get_default();
GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
if (visual != NULL && gdk_screen_is_composited(screen)) {
gtk_widget_set_visual(win, visual);
}
}
int main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *lbl;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
setup(window);
lbl = gtk_label_new("ZetCode, tutorials for programmers");
PangoFontDescription *fd = pango_font_description_from_string("Serif 20");
gtk_widget_modify_font(lbl, fd);
gtk_container_add(GTK_CONTAINER(window), lbl);
GdkColor color;
gdk_color_parse("white", &color);
gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, &color);
g_signal_connect(G_OBJECT(window), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 350, 250);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
该代码在根窗口上显示消息标签。
static void do_drawing(cairo_t *cr)
{
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
cairo_paint(cr);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
}
我们使用CAIRO_OPERATOR_CLEAR
运算符清除窗口背景。 然后我们设置CAIRO_OPERATOR_OVER
以绘制标签窗口小部件。
gtk_widget_set_app_paintable(win, TRUE);
我们将操纵应用窗口,因此我们使其可绘制。
gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_DOCK);
实现此窗口提示会删除窗口边框和装饰。
gtk_window_set_keep_below(GTK_WINDOW(win), TRUE);
我们始终将应用始终放在根窗口的底部。
GdkScreen *screen = gdk_screen_get_default();
GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
if (visual != NULL && gdk_screen_is_composited(screen)) {
gtk_widget_set_visual(win, visual);
}
我们将屏幕的外观设置为应用的外观。
lbl = gtk_label_new("ZetCode, tutorials for programmers");
我们创建一个消息标签。
PangoFontDescription *fd = pango_font_description_from_string("Serif 20");
gtk_widget_modify_font(lbl, fd);
在 Pango 模块的帮助下,我们为文本选择特定的字体。
gtk_container_add(GTK_CONTAINER(window), lbl);
标签贴在窗户上。
GdkColor color;
gdk_color_parse("white", &color);
gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, &color);
我们将文本修改为白色。
图:根窗口上的消息
在本章中,我们使用了 Cairo 的桌面窗口。
PyCairo 教程
原文: https://zetcode/gfx/pycairo/
这是 PyCairo 教程。 在本教程中,我们将学习 Python 和 Cairo 库中的 2D 图形编程。
目录
- 简介
- 后端
- 基本绘图
- 形状&填充
- 渐变
- 剪裁&遮罩
- 透明度
- 转换
- 文字
- 图片
- 根窗口
PyCairo
PyCairo 是用于 Cairo 库的 Python 模块。 它是对 Cairo C 库的一组 Python 绑定。 它与 C API 紧密匹配,但需要更多 Python 方式的情况除外。
相关教程
Cairo 图形教程涵盖了使用 C 编程语言编写的 Cairo 库。
PyCairo 简介
原文: https://zetcode/gfx/pycairo/introduction/
这是 PyCairo 教程。 本教程将使用 Python 语言教您 Cairo 2D 库的基础知识和一些高级主题。 在大多数示例中,我们将使用 Python GTK 后端生成我们的输出。 可以在此处下载本教程中使用的图像。
计算机图像
有两种不同的计算机图形:矢量图形和栅格图形。 光栅图形将图像表示为像素集合。 矢量图形是使用几何图元(例如点,线,曲线或多边形)来表示图像。 这些基元是使用数学方程式创建的。
两种类型的计算机图形都有优点和缺点。 矢量图形优于栅格的优点是:
- 较小的大小
- 无限放大的能力
- 移动,缩放,填充和旋转不会降低图像质量
Cairo
Cairo 是用于创建 2D 矢量图形的库。 它是用 C 编程语言编写的。 存在其他计算机语言的绑定,包括 Python,Perl,C++ ,C# 或 Java。 Cairo 是一个多平台库; 它适用于 Linux,BSD,Windows 和 OSX。
Cairo 支持各种后端。 后端是用于显示创建的图形的输出设备。
- X Window 系统
- Win32 GDI
- Mac OS X
- PNG
- PDF 格式
- PostScript
- SVG
这意味着我们可以使用该库在 Windows,Linux,Windows,OSX 上进行绘制,并且可以使用该库创建 PNG 图像,PDF 文件,PostScript 文件和 SVG 文件。
我们可以将 Cairo 库与 Windows OS 上的 GDI+ 库和 Mac OS 上的 Quartz 2D 比较。 Cairo 是一个开源软件库。 从 2.8 版开始,Cairo 是 GTK 系统的一部分。
定义
在这里,我们提供了一些有用的定义。 要在 PyCairo 中进行绘制,我们首先必须创建一个绘制上下文。 绘图上下文包含所有描述绘图方式的图形状态参数。 这包括诸如线宽,颜色,要绘制的表面以及许多其他信息。 它允许实际的绘图函数采用较少的参数来简化界面。
路径是用于创建基本形状(如直线,圆弧和曲线)的点的集合。 路径有两种:开放路径和封闭路径。 在封闭的路径中,起点和终点相遇。 在开放路径中,起点和终点不相交。 在 PyCairo 中,我们从空路径开始。 首先,我们定义一条路径,然后通过抚摸和/或填充它们使它们可见。 在每个stroke()
或fill()
方法调用之后,该路径将被清空。 我们必须定义一条新路径。 如果我们想保留现有路径以用于以后的绘图,则可以使用stroke_preserve()
和fill_preserve()
方法。 路径由子路径组成。
源是我们在绘图中使用的油漆。 我们可以将源与用来绘制轮廓和填充形状的钢笔或墨水进行比较。 有四种基本来源:颜色,渐变,图案和图像。
曲面是我们要绘制的目标。 我们可以使用 PDF 或 PostScript 曲面渲染文档,并通过 Xlib 和 Win32 曲面直接绘制到平台上。
在将源应用于表面之前,先对其进行过滤。 掩码用作过滤器。 它确定在哪里应用源,不在哪里应用。 遮罩的不透明部分允许复制源。 透明零件不允许将源复制到表面。
模式代表在表面上绘制时的光源。 在 PyCairo 中,图案是您可以读取的东西,用作绘制操作的源或蒙版。 图案可以是实心的,基于表面的或渐变的。
数据来源
为了创建本教程,我们使用了以下材料。 Apple Cocoa 制图指南, PyCairo 参考和Cairo 文档。
PyCairo 后端
原文: https://zetcode/gfx/pycairo/backends/
PyCairo 支持各种后端。 后端是可以显示 PyCairo 产生的图形的地方。 我们使用 PyCairo 创建 PNG 图像,PDF 文件,SVG 文件,然后在 GTK 窗口上绘制。
PNG 图像
在第一个示例中,我们创建一个 PNG 图像。
pngimage.py
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
This program uses PyCairo to
produce a PNG image.
Author: Jan Bodnar
Website: zetcode
Last edited: April 2016
'''
import cairo
def main():
ims = cairo.ImageSurface(cairo.FORMAT_ARGB32, 390, 60)
cr = cairo.Context(ims)
cr.set_source_rgb(0, 0, 0)
cr.select_font_face("Sans", cairo.FONT_SLANT_NORMAL,
cairo.FONT_WEIGHT_NORMAL)
cr.set_font_size(40)
cr.move_to(10, 50)
cr.show_text("Disziplin ist Macht.")
ims.write_to_png("image.png")
if __name__ == "__main__":
main()
此示例是一个小型控制台应用,可创建 PNG 图像。
import cairo
我们导入 PyCairo 模块。
ims = cairo.ImageSurface(cairo.FORMAT_ARGB32, 390, 60)
cr = cairo.Context(ims)
我们创建一个曲面,并从该曲面创建一个 Cairo 上下文。 表面是 390x60 像素的图像。
cr.set_source_rgb(0, 0, 0)
我们用黑色墨水绘制文本。 墨水通过set_source_rgb()
方法指定。
cr.select_font_face("Sans", cairo.FONT_SLANT_NORMAL,
cairo.FONT_WEIGHT_NORMAL)
cr.set_font_size(40)
我们通过select_font_face()
方法选择一种字体类型,并通过set_font_size()
方法设置字体大小。
cr.move_to(10, 50)
cr.show_text("Disziplin ist Macht.")
我们将图像内的位置移至x = 10.0
,y = 50.0
并绘制文本。
ims.write_to_png("image.png")
write_to_png()
方法将表面的内容写入 PNG 图像。
图:Gnome之眼中的 PNG 图像
PDF 文件
在第二个示例中,我们创建一个简单的 PDF 文件。
pdffile.py
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
This program uses PyCairo to
produce a PDF image.
Author: Jan Bodnar
Website: zetcode
Last edited: April 2016
'''
import cairo
def main():
ps = cairo.PDFSurface("pdffile.pdf", 504, 648)
cr = cairo.Context(ps)
cr.set_source_rgb(0, 0, 0)
cr.select_font_face("Sans", cairo.FONT_SLANT_NORMAL,
cairo.FONT_WEIGHT_NORMAL)
cr.set_font_size(40)
cr.move_to(10, 50)
cr.show_text("Disziplin ist Macht.")
cr.show_page()
if __name__ == "__main__":
main()
我们必须在 PDF 查看器中打开 PDF 文件。 Linux 用户可以使用 KPDF 或 Evince 查看器。
ps = cairo.PDFSurface("pdffile.pdf", 504, 648)
要渲染 PDF 文件,我们必须使用cairo.PDFSurface
对象创建 PDF 曲面。 PDF 文件的大小以磅为单位指定,这是排版的标准。
cr.show_page()
show_page()
完成 PDF 文件的渲染。
图:Evince 中的 PDF 文件
SVG 文件
下一个示例创建一个简单的 SVG(可缩放矢量图形)文件。 SVG 文件是基于 XML 的文件格式。
svgfile.py
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
This program uses PyCairo to
produce a SVG file.
Author: Jan Bodnar
Website: zetcode
Last edited: April 2016
'''
import cairo
def main():
ps = cairo.SVGSurface("svgfile.svg", 390, 60)
cr = cairo.Context(ps)
cr.set_source_rgb(0, 0, 0)
cr.select_font_face("Sans", cairo.FONT_SLANT_NORMAL,
cairo.FONT_WEIGHT_NORMAL)
cr.set_font_size(40)
cr.move_to(10, 50)
cr.show_text("Disziplin ist Macht.")
cr.show_page()
if __name__ == "__main__":
main()
我们可以使用网络浏览器(例如 Google Chrome)或矢量绘图程序(例如 Inkscape)打开 SVG 文件。
ps = cairo.SVGSurface("svgfile.svg", 390, 60)
要在 PyCairo 中创建 SVG 文件,我们必须使用cairo.SVGSurface
对象创建 SVG 表面。
cr.show_page()
show_page()
方法调用完成了 SVG 文件的呈现。
SVG file in Chrome
GTK 窗口
在最后一个示例中,我们在 GTK 窗口上绘制。 该后端将在本教程的其余部分中使用。
gtkwindow.py
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
This program uses PyCairo to
draw on a window in GTK.
Author: Jan Bodnar
Website: zetcode
Last edited: April 2016
'''
from gi.repository import Gtk
import cairo
class Example(Gtk.Window):
def __init__(self):
super(Example, self).__init__()
self.init_ui()
def init_ui(self):
darea = Gtk.DrawingArea()
darea.connect("draw", self.on_draw)
self.add(darea)
self.set_title("GTK window")
self.resize(420, 120)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
self.show_all()
def on_draw(self, wid, cr):
cr.set_source_rgb(0, 0, 0)
cr.select_font_face("Sans", cairo.FONT_SLANT_NORMAL,
cairo.FONT_WEIGHT_NORMAL)
cr.set_font_size(40)
cr.move_to(10, 50)
cr.show_text("Disziplin ist Macht.")
def main():
app = Example()
Gtk.main()
if __name__ == "__main__":
main()
该示例弹出一个居中的 GTK 窗口,在该窗口上绘制"Disziplin ist Macht"
文本。
from gi.repository import Gtk
import cairo
我们导入必要的 PyCairo 和 GTK 模块。
darea = Gtk.DrawingArea()
我们将使用Gtk.DrawingArea
小部件。
darea.connect("draw", self.on_draw)
重新绘制窗口时,会发出draw
信号。 我们将该信号连接到on_draw()
回调。
def on_draw(self, wid, cr):
...
绘图是在on_draw()
方法内部完成的。 第三个参数是 Cairo 上下文。 它是自动提供给我们的; Cairo 库内置在 GTK 系统中。
图:GTK 窗口
在本章中,我们介绍了受支持的 PyCairo 后端。
PyCairo 中的基本绘图
原文: https://zetcode/gfx/pycairo/basicdrawing/
在 PyCairo 教程的这一部分中,我们绘制了一些基本图元。 我们使用填充和描边操作,笔划线,线帽和线连接。
直线
线是非常基本的矢量对象。 要画一条线,我们使用两个方法调用。 通过move_to()
调用指定起点。 线的终点通过line_to()
调用指定。
lines.py
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
In this program, we connect all mouse
clicks with a line.
Author: Jan Bodnar
Website: zetcode
Last edited: April 2016
'''
from gi.repository import Gtk, Gdk
import cairo
class MouseButtons:
LEFT_BUTTON = 1
RIGHT_BUTTON = 3
class 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.darea.set_events(Gdk.EventMask.BUTTON_PRESS_MASK)
self.add(self.darea)
self.coords = []
self.darea.connect("button-press-event", self.on_button_press)
self.set_title("Lines")
self.resize(300, 200)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
self.show_all()
def on_draw(self, wid, cr):
cr.set_source_rgb(0, 0, 0)
cr.set_line_width(0.5)
for i in self.coords:
for j in self.coords:
cr.move_to(i[0], i[1])
cr.line_to(j[0], j[1])
cr.stroke()
del self.coords[:]
def on_button_press(self, w, e):
if e.type == Gdk.EventType.BUTTON_PRESS \
and e.button == MouseButtons.LEFT_BUTTON:
self.coords.append([e.x, e.y])
if e.type == Gdk.EventType.BUTTON_PRESS \
and e.button == MouseButtons.RIGHT_BUTTON:
self.darea.queue_draw()
def main():
app = Example()
Gtk.main()
if __name__ == "__main__":
main()
在我们的示例中,我们用鼠标左键随机单击窗口。 每次点击都存储在一个列表中。 当我们右键单击窗口时,所有点都与列表中的每个其他点相连。 右键单击将清除窗口。
class MouseButtons:
LEFT_BUTTON = 1
RIGHT_BUTTON = 3
GTK 文档仅声明鼠标左键的编号为 1,鼠标右键的编号为 3。我们创建了一个自定义类,其中包含一些鼠标键的标识符。
self.darea.set_events(Gdk.EventMask.BUTTON_PRESS_MASK)
默认情况下,某些事件未启用。 鼠标按下事件就在其中。 因此,我们需要使用set_event()
方法启用鼠标按下事件。
self.darea.connect("button-press-event", self.on_button_press)
在此代码示例中,我们对鼠标按下事件做出反应。
cr.set_source_rgb(0, 0, 0)
cr.set_line_width(0.5)
线条以黑色墨水绘制,宽度为 0.5 点。
for i in self.coords:
for j in self.coords:
cr.move_to(i[0], i[1])
cr.line_to(j[0], j[1])
cr.stroke()
我们将列表中的每个点连接到其他每个点。 stroke()
调用画线。
del self.coords[:]
最后,将删除所有坐标。 现在,我们可以创建另一个对象。
def on_button_press(self, w, e):
if e.type == Gdk.EventType.BUTTON_PRESS \
and e.button == MouseButtons.LEFT_BUTTON:
self.coords.append([e.x, e.y])
...
如果按下鼠标左键,我们会将其 x 和 y 坐标添加到self.coords
列表中。
if e.type == Gdk.EventType.BUTTON_PRESS \
and e.button == MouseButtons.RIGHT_BUTTON:
self.darea.queue_draw()
在按下鼠标右键的情况下,我们调用queue_draw()
方法来重绘绘图区域。 所有的点都用线连接。
图:直线
填充和描边
描边操作绘制形状的轮廓,填充操作填充形状的内部。
fillstroke.py
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
This code example draws a circle
using the PyCairo library.
Author: Jan Bodnar
Website: zetcode
Last edited: April 2016
'''
from gi.repository import Gtk
import cairo
import math
class Example(Gtk.Window):
def __init__(self):
super(Example, self).__init__()
self.init_ui()
def init_ui(self):
darea = Gtk.DrawingArea()
darea.connect("draw", self.on_draw)
self.add(darea)
self.set_title("Fill & stroke")
self.resize(230, 150)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
self.show_all()
def on_draw(self, wid, cr):
cr.set_line_width(9)
cr.set_source_rgb(0.7, 0.2, 0.0)
w, h = self.get_size()
cr.translate(w/2, h/2)
cr.arc(0, 0, 50, 0, 2*math.pi)
cr.stroke_preserve()
cr.set_source_rgb(0.3, 0.4, 0.6)
cr.fill()
def main():
app = Example()
Gtk.main()
if __name__ == "__main__":
main()
在示例中,我们绘制了一个圆圈,并用纯色填充。
import math
用于绘制圆的pi
常数需要此模块。
cr.set_line_width(9)
cr.set_source_rgb(0.7, 0.2, 0.0)
我们使用set_line_width()
方法设置线宽。 我们使用set_source_rgb()
方法将光源设置为深红色。
w, h = self.get_size()
在这里,我们获得了窗口的宽度和高度。 我们需要这些值使圆在窗口上居中。
cr.translate(w/2, h/2)
cr.arc(0, 0, 50, 0, 2*math.pi)
cr.stroke_preserve()
使用translate()
方法,我们将图形原点移动到窗口的中心。 我们希望我们的圈子居中。 arc()
方法向 Cairo 图形上下文添加了新的圆形路径。 最后,stroke_preserve()
方法绘制圆的轮廓。 与stroke()
方法不同,它还保留了形状以供以后绘制。
cr.set_source_rgb(0.3, 0.4, 0.6)
cr.fill()
我们使用fill()
方法更改绘制颜色,并用新颜色填充圆。
图:填充和描边
笔划线
每条线可以用不同的笔划线绘制。 笔笔划线定义线条的样式。 笔划线由set_dash()
方法指定。 该模式由笔划线列表设置,笔划线列表是浮点值的列表。 他们设置笔划线图案的开和关部分。 stroke()
方法使用笔划线创建一条线。 如果笔划线为 0,则禁用笔划线。 如果笔划线的数量为 1,则假定使用对称模式,其中交替的开和关部分由笔划线中的单个值指定。
def on_draw(self, wid, cr):
cr.set_source_rgba(0, 0, 0, 1)
cr.set_line_width(2)
cr.set_dash([4.0, 21.0, 2.0])
cr.move_to(40, 30)
cr.line_to(250, 30)
cr.stroke()
cr.set_dash([14.0, 6.0])
cr.move_to(40, 50)
cr.line_to(250, 50)
cr.stroke()
cr.set_dash([1.0])
cr.move_to(40, 70)
cr.line_to(250, 70)
cr.stroke()
我们用三个不同的笔划线画了三条线。
cr.set_dash([4.0, 21.0, 2.0])
我们有三个数字的模式。 我们绘制了 4 个点,未绘制 21 个,绘制了 2 个点,然后绘制了 4 个点,绘制了 21 个点。 和 2 未绘制。 该模式轮流直到行尾。
cr.set_dash([14.0, 6.0])
在这种模式下,我们总是绘制 14 点,未绘制 6 点。
cr.set_dash([1.0])
在这里,我们创建了一个对称图案的笔划线,该图案交替出现单个接通和断开点。
图:笔划线
线帽
线帽是线的端点。
Cairo.LINE_CAP_BUTT
Cairo.LINE_CAP_ROUND
Cairo.LINE_CAP_SQUARE
Cairo 有三种不同的线帽样式。
图:正方形,圆和端帽
带有cairo.LINE_CAP_SQUARE
帽的线的大小与带有cairo.LINE_CAP_BUTT
帽的线的大小不同。 如果一行的宽度是 x 单位,则带cairo.LINE_CAP_SQUARE
上限的行的大小将恰好是 x 单位; 开头x / 2
个单位,结尾x / 2
个单位。
def on_draw(self, wid, cr):
cr.set_source_rgba(0, 0, 0, 1)
cr.set_line_width(12)
cr.set_line_cap(cairo.LINE_CAP_BUTT)
cr.move_to(30, 50)
cr.line_to(150, 50)
cr.stroke()
cr.set_line_cap(cairo.LINE_CAP_ROUND)
cr.move_to(30, 90)
cr.line_to(150, 90)
cr.stroke()
cr.set_line_cap(cairo.LINE_CAP_SQUARE)
cr.move_to(30, 130)
cr.line_to(150, 130)
cr.stroke()
cr.set_line_width(1.5)
cr.move_to(30, 35)
cr.line_to(30, 145)
cr.stroke()
cr.move_to(150, 35)
cr.line_to(150, 145)
cr.stroke()
cr.move_to(155, 35)
cr.line_to(155, 145)
cr.stroke()
该示例绘制具有三个不同线帽的三条线。 通过绘制三条额外的细垂直线,它还将以图形方式显示线的大小差异。
cr.set_line_width(12)
我们的生产线将是 12 个单位宽。 默认线宽为 2。
cr.set_line_cap(cairo.LINE_CAP_ROUND)
cr.move_to(30, 90)
cr.line_to(150, 90)
cr.stroke()
在这里,我们用cairo.LINE_CAP_ROUND
帽画一条水平线。
cr.set_line_width(1.5)
cr.move_to(30, 35)
cr.line_to(30, 145)
cr.stroke()
这是用来说明大小差异的三条垂直线之一。
图:直线的端帽
线连接
可以使用三种不同的连接样式来连接线。
Cairo.LINE_JOIN_MITER
Cairo.LINE_JOIN_BEVEL
Cairo.LINE_JOIN_ROUND
图:斜角,圆角,斜接线连接
def on_draw(self, wid, cr):
cr.set_line_width(14)
cr.rectangle(30, 30, 100, 100)
cr.set_line_join(cairo.LINE_JOIN_MITER)
cr.stroke()
cr.rectangle(160, 30, 100, 100)
cr.set_line_join(cairo.LINE_JOIN_BEVEL)
cr.stroke()
cr.rectangle(100, 160, 100, 100)
cr.set_line_join(cairo.LINE_JOIN_ROUND)
cr.stroke()
在此示例中,我们绘制了三个具有各种线连接的粗矩形。
cr.set_line_width(14)
线宽为 14 个单位。
cr.rectangle(30, 30, 100, 100)
cr.set_line_join(cairo.LINE_JOIN_MITER)
cr.stroke()
在这里,我们绘制一个具有cairo.LINE_JOIN_MITER
连接样式的矩形。
图:直线连接
贝塞尔曲线
贝塞尔曲线是由数学公式定义的曲线。 绘制曲线的数学方法由 PierreBézier 在 1960 年代后期创建,用于雷诺的汽车制造。
curve_to(x1, y1, x2, y2, x3, y3)
curve_to()
方法将三次贝塞尔曲线样条添加到路径。 参数为第一控制点的 x 和 y 坐标,第二控制点的 x 和 y 坐标以及曲线末端的 x 和 y 坐标。
def on_draw(self, wid, cr):
cr.move_to(20, 40)
cr.curve_to(320, 200, 330, 110, 450, 40)
cr.stroke()
在此示例中,使用curve_to()
方法绘制了贝塞尔曲线。
图:贝塞尔曲线
在 PyCairo 教程的这一章中,我们做了一些基本的绘制。
PyCairo 形状和填充
原文: https://zetcode/gfx/pycairo/shapesfills/
在 PyCairo 教程的这一部分中,我们创建一些基本的和更高级的形状。 我们用纯色,图案和渐变填充这些形状。 渐变将在单独的章节中介绍。
基本形状
PyCairo 有一些创建简单形状的基本方法。
def on_draw(self, wid, cr):
cr.set_source_rgb(0.6, 0.6, 0.6)
cr.rectangle(20, 20, 120, 80)
cr.rectangle(180, 20, 80, 80)
cr.fill()
cr.arc(330, 60, 40, 0, 2*math.pi)
cr.fill()
cr.arc(90, 160, 40, math.pi/4, math.pi)
cr.fill()
cr.translate(220, 180)
cr.scale(1, 0.7)
cr.arc(0, 0, 50, 0, 2*math.pi)
cr.fill()
在此示例中,我们创建一个矩形,一个正方形,一个圆形,一个弧形和一个椭圆形。
cr.rectangle(20, 20, 120, 80)
cr.rectangle(180, 20, 80, 80)
rectangle()
方法用于创建正方形和矩形。 正方形只是矩形的一种特定类型。 参数是窗口左上角的 x 和 y 坐标以及矩形的宽度和高度。
cr.arc(330, 60, 40, 0, 2*math.pi)
arc()
方法创建一个圆。 参数是弧度中心的 x 和 y 坐标,半径以及弧度的开始和结束角度。
cr.arc(90, 160, 40, math.pi/4, math.pi)
在这里,我们画一条弧,是圆的一部分。
cr.scale(1, 0.7)
cr.arc(0, 0, 50, 0, 2*math.pi)
我们使用scale()
和arc()
方法创建一个椭圆。
图:基本形状
可以使用基本图元的组合来创建其他形状。
complex_shapes.py
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
This code example draws another
three shapes in PyCairo.
Author: Jan Bodnar
Website: zetcode
Last edited: April 2016
'''
from gi.repository import Gtk
import cairo
class cv(object):
points = (
( 0, 85 ),
( 75, 75 ),
( 100, 10 ),
( 125, 75 ),
( 200, 85 ),
( 150, 125 ),
( 160, 190 ),
( 100, 150 ),
( 40, 190 ),
( 50, 125 ),
( 0, 85 )
)
class Example(Gtk.Window):
def __init__(self):
super(Example, self).__init__()
self.init_ui()
def init_ui(self):
darea = Gtk.DrawingArea()
darea.connect("draw", self.on_draw)
self.add(darea)
self.set_title("Complex shapes")
self.resize(460, 240)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
self.show_all()
def on_draw(self, wid, cr):
cr.set_source_rgb(0.6, 0.6, 0.6)
cr.set_line_width(1)
for i in range(10):
cr.line_to(cv.points[i][0], cv.points[i][1])
cr.fill()
cr.move_to(240, 40)
cr.line_to(240, 160)
cr.line_to(350, 160)
cr.fill()
cr.move_to(380, 40)
cr.line_to(380, 160)
cr.line_to(450, 160)
cr.curve_to(440, 155, 380, 145, 380, 40)
cr.fill()
def main():
app = Example()
Gtk.main()
if __name__ == "__main__":
main()
在此示例中,我们创建一个星形对象,一个三角形和一个修改后的三角形。 这些对象是使用直线和一条曲线创建的。
for i in range(10):
cr.line_to(cv.points[i][0], cv.points[i][1])
cr.fill()
通过连接点元组中的所有点来绘制星形。 fill()
方法用当前颜色填充星形对象。
cr.move_to(240, 40)
cr.line_to(240, 160)
cr.line_to(350, 160)
cr.fill()
这些线创建一个三角形。 最后两点将自动合并。
cr.move_to(380, 40)
cr.line_to(380, 160)
cr.line_to(450, 160)
cr.curve_to(440, 155, 380, 145, 380, 40)
cr.fill()
修改后的三角形是两条直线和一条曲线的简单组合。
图:复杂形状
填充
填充填充形状的内部。 填充可以是纯色,图案或渐变。
纯色
颜色是代表红色,绿色和蓝色(RGB)强度值的组合的对象。 PyCairo 的有效 RGB 值在 0 到 1 的范围内。
def on_draw(self, wid, cr):
cr.set_source_rgb(0.2, 0.23, 0.9)
cr.rectangle(10, 15, 90, 60)
cr.fill()
cr.set_source_rgb(0.9, 0.1, 0.1)
cr.rectangle(130, 15, 90, 60)
cr.fill()
cr.set_source_rgb(0.4, 0.9, 0.4)
cr.rectangle(250, 15, 90, 60)
cr.fill()
在示例中,我们绘制了四个彩色矩形。
cr.set_source_rgb(0.2, 0.23, 0.9)
cr.rectangle(10, 15, 90, 60)
cr.fill()
set_source_rgb()
方法将源设置为不透明的颜色。 参数是红色,绿色,蓝色强度值。 通过调用fill()
方法,该源用于填充矩形的内部。
图:纯色
图案
图案是可以用于填充形状的复杂图形对象。
patterns.py
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
This program shows how to work
with patterns in PyCairo.
Author: Jan Bodnar
Website: zetcode
Last edited: April 2016
'''
from gi.repository import Gtk
import cairo
class Example(Gtk.Window):
def __init__(self):
super(Example, self).__init__()
self.init_ui()
self.create_surpat()
def init_ui(self):
darea = Gtk.DrawingArea()
darea.connect("draw", self.on_draw)
self.add(darea)
self.set_title("Patterns")
self.resize(300, 290)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
self.show_all()
def create_surpat(self):
sr1 = cairo.ImageSurface.create_from_png("blueweb.png")
sr2 = cairo.ImageSurface.create_from_png("maple.png")
sr3 = cairo.ImageSurface.create_from_png("crack.png")
sr4 = cairo.ImageSurface.create_from_png("chocolate.png")
self.pt1 = cairo.SurfacePattern(sr1)
self.pt1.set_extend(cairo.EXTEND_REPEAT)
self.pt2 = cairo.SurfacePattern(sr2)
self.pt2.set_extend(cairo.EXTEND_REPEAT)
self.pt3 = cairo.SurfacePattern(sr3)
self.pt3.set_extend(cairo.EXTEND_REPEAT)
self.pt4 = cairo.SurfacePattern(sr4)
self.pt4.set_extend(cairo.EXTEND_REPEAT)
def on_draw(self, wid, cr):
cr.set_source(self.pt1)
cr.rectangle(20, 20, 100, 100)
cr.fill()
cr.set_source(self.pt2)
cr.rectangle(150, 20, 100, 100)
cr.fill()
cr.set_source(self.pt3)
cr.rectangle(20, 140, 100, 100)
cr.fill()
cr.set_source(self.pt4)
cr.rectangle(150, 140, 100, 100)
cr.fill()
def main():
app = Example()
Gtk.main()
if __name__ == "__main__":
main()
在此示例中,我们绘制了四个矩形。 这次我们用一些模式填充它们。 我们使用来自 Gimp 图像处理器的四个图案图像。 我们必须保留这些图案的原始大小,因为我们将它们平铺。
我们在draw()
方法之外创建图像表面。 每次需要重新绘制窗口时,从硬盘读取数据都不会很有效。
sr1 = cairo.ImageSurface.create_from_png("blueweb.png")
从 PNG 图像创建图像表面。
self.pt1 = cairo.SurfacePattern(sr1)
self.pt1.set_extend(cairo.EXTEND_REPEAT)
从表面创建图案。 我们将模式设置为cairo.EXTEND_REPEAT
,这将导致图案通过重复平铺。
cr.set_source(self.pt1)
cr.rectangle(20, 20, 100, 100)
cr.fill()
在这里,我们绘制第一个矩形。 set_source()
方法告诉 Cairo 上下文使用图案作为绘图源。 图像图案可能不完全适合形状。 rectangle()
创建一个矩形路径。 最后,fill()
方法用源填充路径。
本章介绍了 PyCairo 的形状和填充。
PyCairo 渐变
原文: https://zetcode/gfx/pycairo/gradients/
在 PyCairo 教程的这一部分中,我们将介绍渐变。 我们将提到线性和径向渐变。
在计算机图形学中,渐变是从浅到深或从一种颜色到另一种颜色的阴影的平滑混合。 在 2D 绘图程序和绘画程序中,渐变用于创建彩色背景和特殊效果以及模拟灯光和阴影。 (answers)
线性渐变
线性渐变是沿着一条线的颜色混合或颜色阴影混合。 它们由 PyCairo 中的cairo.LinearGradient
类表示。
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
This program works with linear
gradients in PyCairo.
author: Jan Bodnar
website: zetcode
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()
def init_ui(self):
darea = Gtk.DrawingArea()
darea.connect("draw", self.on_draw)
self.add(darea)
self.set_title("Linear gradients")
self.resize(340, 390)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
self.show_all()
def on_draw(self, wid, cr):
self.draw_gradient1(cr)
self.draw_gradient2(cr)
self.draw_gradient3(cr)
def draw_gradient1(self, cr):
lg1 = cairo.LinearGradient(0.0, 0.0, 350.0, 350.0)
count = 1
i = 0.1
while i < 1.0:
if count % 2:
lg1.add_color_stop_rgba(i, 0, 0, 0, 1)
else:
lg1.add_color_stop_rgba(i, 1, 0, 0, 1)
i = i + 0.1
count = count + 1
cr.rectangle(20, 20, 300, 100)
cr.set_source(lg1)
cr.fill()
def draw_gradient2(self, cr):
lg2 = cairo.LinearGradient(0.0, 0.0, 350.0, 0)
count = 1
i = 0.05
while i < 0.95:
if count % 2:
lg2.add_color_stop_rgba(i, 0, 0, 0, 1)
else:
lg2.add_color_stop_rgba(i, 0, 0, 1, 1)
i = i + 0.025
count = count + 1
cr.rectangle(20, 140, 300, 100)
cr.set_source(lg2)
cr.fill()
def draw_gradient3(self, cr):
lg3 = cairo.LinearGradient(20.0, 260.0, 20.0, 360.0)
lg3.add_color_stop_rgba(0.1, 0, 0, 0, 1)
lg3.add_color_stop_rgba(0.5, 1, 1, 0, 1)
lg3.add_color_stop_rgba(0.9, 0, 0, 0, 1)
cr.rectangle(20, 260, 300, 100)
cr.set_source(lg3)
cr.fill()
def main():
app = Example()
Gtk.main()
if __name__ == "__main__":
main()
该示例绘制了三个填充有线性渐变的矩形。
lg3 = cairo.LinearGradient(20.0, 260.0, 20.0, 360.0)
在这里,我们创建一个线性渐变。 参数指定直线,沿着该直线绘制渐变。 这是一条水平线。
lg3.add_color_stop_rgba(0.1, 0, 0, 0, 1)
lg3.add_color_stop_rgba(0.5, 1, 1, 0, 1)
lg3.add_color_stop_rgba(0.9, 0, 0, 0, 1)
我们定义色标以产生渐变图案。 在这种情况下,渐变是黑色和黄色的混合。 通过添加两个黑色和一个黄色色标,我们创建了一个水平渐变图案。 这些停止实际上是什么意思? 在我们的情况下,我们从黑色开始,该黑色将停止在大小的 1/10 处。 然后,我们开始逐渐涂成黄色,最终达到形状的中心。 黄色停在大小的 9/10,我们再次开始用黑色绘画,直到结束。
图:线性渐变
径向渐变
径向渐变是两个圆之间颜色或阴影的混合。 cairo.RadialGradient
类用于在 PyCairo 中创建径向渐变。
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
This program works with radial
gradients in PyCairo.
author: Jan Bodnar
website: zetcode
last edited: August 2012
'''
from gi.repository import Gtk
import cairo
import math
class Example(Gtk.Window):
def __init__(self):
super(Example, self).__init__()
self.init_ui()
def init_ui(self):
darea = Gtk.DrawingArea()
darea.connect("draw", self.on_draw)
self.add(darea)
self.set_title("Radial gradients")
self.resize(300, 200)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
self.show_all()
def on_draw(self, wid, cr):
self.draw_gradient1(cr)
self.draw_gradient2(cr)
def draw_gradient1(self, cr):
cr.set_source_rgba(0, 0, 0, 1)
cr.set_line_width(12)
cr.translate(60, 60)
r1 = cairo.RadialGradient(30, 30, 10, 30, 30, 90)
r1.add_color_stop_rgba(0, 1, 1, 1, 1)
r1.add_color_stop_rgba(1, 0.6, 0.6, 0.6, 1)
cr.set_source(r1)
cr.arc(0, 0, 40, 0, math.pi * 2)
cr.fill()
cr.translate(120, 0)
def draw_gradient2(self, cr):
r2 = cairo.RadialGradient(0, 0, 10, 0, 0, 40)
r2.add_color_stop_rgb(0, 1, 1, 0)
r2.add_color_stop_rgb(0.8, 0, 0, 0)
cr.set_source(r2)
cr.arc(0, 0, 40, 0, math.pi * 2)
cr.fill()
def main():
app = Example()
Gtk.main()
if __name__ == "__main__":
main()
在示例中,我们绘制了两个径向渐变。
r1 = cairo.RadialGradient(30, 30, 10, 30, 30, 90)
r1.add_color_stop_rgba(0, 1, 1, 1, 1)
r1.add_color_stop_rgba(1, 0.6, 0.6, 0.6, 1)
cr.set_source(r1)
cr.arc(0, 0, 40, 0, math.pi * 2)
cr.fill()
我们画一个圆,并用径向渐变填充其内部。 径向梯度由两个圆定义。 add_color_stop_rgba()
方法定义颜色。 我们可以试验圆的位置或半径的长度。 在第一个渐变示例中,我们创建了一个类似于 3D 形状的对象。
r2 = cairo.RadialGradient(0, 0, 10, 0, 0, 40)
r2.add_color_stop_rgb(0, 1, 1, 0)
r2.add_color_stop_rgb(0.8, 0, 0, 0)
cr.set_source(r2)
cr.arc(0, 0, 40, 0, math.pi * 2)
cr.fill()
在此示例中,定义径向渐变的圆和自定义绘制的圆具有共同的中心点。
图:径向渐变
在本章中,我们介绍了 PyCairo 渐变。
PyCairo 剪裁&遮罩
原文: https://zetcode/gfx/pycairo/clipmask/
在 PyCairo 教程的这一部分中,我们将讨论剪切和遮罩操作。
剪裁
剪裁是将绘图限制为特定区域。 这样做是出于效率方面的考虑,并会产生有趣的效果。 PyCairo 具有clip()
方法来设置裁剪。
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
This program shows how to perform
clipping in PyCairo.
author: Jan Bodnar
website: zetcode
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):
self.image = cairo.ImageSurface.create_from_png("beckov.png")
这是基础图像。 每个计时器周期,我们都会看到此图像的一部分。
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)
如果圆碰到窗口的左侧或右侧,则圆的移动方向会随机变化。 顶部和底部也一样。
cr.arc(self.pos_x, self.pos_y, self.radius, 0, 2*math.pi)
此行为 Cairo 上下文添加了一条循环路径。
cr.clip()
clip()
设置剪切区域。 裁剪区域是当前使用的路径。 当前路径是通过arc()
方法调用创建的。
cr.paint()
paint()
在当前剪裁区域内的任何地方绘制当前源。
图:剪裁
遮罩
在将源应用于表面之前,先对其进行过滤。 遮罩用作过滤器。 遮罩确定在哪里应用源,在哪里不应用。 遮罩的不透明部分允许复制源。 透明零件不允许将源复制到表面。
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
This program demonstrates masking.
author: Jan Bodnar
website: zetcode
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);
cr.fill()
我们使用图像作为遮罩,从而将其显示在窗口上。
图:遮罩
蒙蔽效果
在此代码示例中,我们将忽略图像。 这类似于我们使用卷帘所做的。
#!/usr/bin/python
'''
ZetCode PyCairo tutorial
This program creates a blind down
effect using masking operation.
author: Jan Bodnar
website: zetcode
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()
盲目效应背后的想法很简单。 图像高度为 h 像素。 我们绘制高度为 1px 的 0、1、2 … 线。 每个周期,图像的一部分高 1px,直到整个图像可见为止。
def load_image(self):
self.image = cairo.ImageSurface.create_from_png("beckov.png")
在load_image()
方法中,我们从 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)
在init_vars()
方法中,我们初始化一些变量。 我们启动self.timer
和self.h
变量。 我们得到加载图像的宽度和高度。 然后我们创建一个空的图像表面。 它将用我们之前创建的图像表面的像素线填充。
ic = cairo.Context(self.ims)
我们从空图像源创建一个 cairo 上下文。
ic.rectangle(0, 0, self.iw, self.h)
ic.fill()
我们在最初为空的图像中绘制一个矩形。 矩形每个周期将高出 1 像素。 以这种方式创建的图像稍后将用作遮罩。
self.h += 1
要显示的图像高度增加一个单位。
if (self.h == self.ih):
self.timer = False
当我们在 GTK 窗口上绘制整个图像时,我们将停止timer
方法。
cr.set_source_surface(self.image, 10, 10)
cr.mask_surface(self.ims, 10, 10)
城堡的图像被设置为绘画的来源。 mask_surface()
使用表面的 Alpha 通道作为遮罩来绘制电流源。
本章介绍了 PyCairo 中的剪裁和遮罩。
发布者:admin,转转请注明出处:http://www.yc00.com/web/1754948107a5219464.html
评论列表(0条)