這一部分,講述一些基本的以及較為高級的形狀繪製及其純色 (solid color)、圖案 (pattern) 與漸變 (gradient) 填充方法。
基本形狀
Cairo 提供了幾個用於繪製基本形狀的函數。
#include
#include
static gboolean
on_expose_event (GtkWidget * widget,
GdkEventExpose * event, gpointer data)
{
cairo_t *cr;
cr = gdk_cairo_create (widget->window);
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_set_line_width (cr, 1);
cairo_rectangle (cr, 20, 20, 120, 80);
cairo_rectangle (cr, 180, 20, 80, 80);
cairo_stroke_preserve (cr);
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_fill (cr);
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_arc (cr, 330, 60, 40, 0, 2 * M_PI);
cairo_stroke_preserve (cr);
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_fill (cr);
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_arc (cr, 90, 160, 40, M_PI / 4, M_PI);
cairo_close_path (cr);
cairo_stroke_preserve (cr);
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_fill (cr);
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_translate (cr, 220, 180);
cairo_scale (cr, 1, 0.7);
cairo_arc (cr, 0, 0, 50, 0, 2 * M_PI);
cairo_stroke_preserve (cr);
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_fill (cr);
cairo_destroy (cr);
return FALSE;
}
int
main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *darea;
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 (darea, "expose-event",
G_CALLBACK (on_expose_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), 390, 240);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
這個示例,繪製了矩形、正方形、圓、圓弧和橢圓。
下面對關鍵代碼簡單分析:
cairo_rectangle (cr, 180, 20, 80, 80);
繪製矩形與正方形。正方形在 cairo 中是矩形的一種特例。
畫了一個圓,圓心為 (330, 60)px,半徑為 40px。Cairo 所謂的圓,其實是起始角為 0 度,終止角為 360 度的弧線。
cairo_arc (cr, 0, 0, 50, 0, 2 * M_PI);
畫橢圓的方法也與畫圓類似,只是需要先設定長軸與短軸的比例,在本例中為 1:0.7。
複雜的圖形
複雜的圖形是由簡單的圖形拼湊出來的,譬如下面這個繪製圓角矩形的程序。
#include
#include
static void
draw_round_rectangle (cairo_t * cr,
double x, double y,
double width, double height, double r)
{
cairo_move_to (cr, x + r, y);
cairo_line_to (cr, x + width - r, y);
cairo_move_to (cr, x + width, y + r);
cairo_line_to (cr, x + width, y + height - r);
cairo_move_to (cr, x + width - r, y + height);
cairo_line_to (cr, x + r, y + height);
cairo_move_to (cr, x, y + height - r);
cairo_line_to (cr, x, y + r);
cairo_arc (cr, x + r, y + r, r, M_PI, 3 * M_PI / 2.0);
cairo_arc (cr, x + width - r, y + r, r, 3 * M_PI / 2, 2 * M_PI);
cairo_arc (cr, x + width - r, y + height - r, r, 0, M_PI / 2);
cairo_arc (cr, x + r, y + height - r, r, M_PI / 2, M_PI);
}
static gboolean
on_expose_event (GtkWidget * widget,
GdkEventExpose * event, gpointer data)
{
cairo_t *cr;
int width, height;
double w, h, x, y, r;
gtk_window_get_size (GTK_WINDOW (widget), &width, &height);
x = width / 5.0;
y = height / 5.0;
w = 3 * width / 5.0;
h = 3 * height / 5.0;
r = h / 4.0;
cr = gdk_cairo_create (widget->window);
cairo_set_source_rgb (cr, 0.8, 0.4, 0);
cairo_set_line_width (cr, 6);
draw_round_rectangle (cr, x, y, w, h, r);
cairo_stroke_preserve (cr);
cairo_set_source_rgb (cr, 0.8, 0.8, 0.2);
cairo_fill (cr);
cairo_destroy (cr);
g_print ("test\n");
return FALSE;
}
static gboolean
on_configure_event (GtkWidget * widget,
GdkEventConfigure * event, gpointer data)
{
gdk_window_invalidate_rect (widget->window,
&widget->allocation,
FALSE);
return FALSE;
}
int
main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *darea;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (window, "expose-event",
G_CALLBACK (on_expose_event), NULL);
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(window), "configure-event",
G_CALLBACK(on_configure_event), NULL);
gtk_window_set_position (GTK_WINDOW (window),
GTK_WIN_POS_CENTER);
gtk_window_set_default_size (GTK_WINDOW (window), 400, 300);
gtk_widget_set_app_paintable (window, TRUE);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
註:因為 "The cairo graphics tutorial" 在這一部分所提供的示例程序不具代表性,因此寫了這個程序。
該示例程序繪製了一個可跟隨窗口尺寸進行縮放變化的圓角矩形。
自定義的 draw_round_rectangle () 函數利用 Cairo 提供的基本圖元函數,利用直線段與圓弧拼湊出圓角矩形。on_configure_event () 函數用於響應窗口尺寸變化事件,在其中調用 gdk_window_invalidate_rect () 函數讓窗口繪圖區域失效,並產生窗口重繪製事件(即 expose 事件)。
填充 (Fill)
雖然上一篇已經講述了一些有關填充的知識,但這裡所講述的內容是與形狀相關的。填充可分為三種類型:純色、圖案、漸變。
純色 (Solid color)
對象的顏色是採用紅 (R)、綠 (G)、藍 (B) 三原色描述的,Cairo 的 RGB 取值是從 0 到 1 的雙精浮點數。
#include
static gboolean
on_expose_event (GtkWidget * widget,
GdkEventExpose * event, gpointer data)
{
cairo_t *cr;
cr = gdk_cairo_create (widget->window);
int width, height;
gtk_window_get_size (GTK_WINDOW (widget), &width, &height);
cairo_set_source_rgb (cr, 0.5, 0.5, 1);
cairo_rectangle (cr, 20, 20, 100, 100);
cairo_fill (cr);
cairo_set_source_rgb (cr, 0.6, 0.6, 0.6);
cairo_rectangle (cr, 150, 20, 100, 100);
cairo_fill (cr);
cairo_set_source_rgb (cr, 0, 0.3, 0);
cairo_rectangle (cr, 20, 140, 100, 100);
cairo_fill (cr);
cairo_set_source_rgb (cr, 1, 0, 0.5);
cairo_rectangle (cr, 150, 140, 100, 100);
cairo_fill (cr);
cairo_destroy (cr);
return FALSE;
}
int
main (int argc, char *argv[])
{
GtkWidget *window;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (G_OBJECT (window), "expose-event",
G_CALLBACK (on_expose_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), 270, 260);
gtk_window_set_title (GTK_WINDOW (window), "colors");
gtk_widget_set_app_paintable (window, TRUE);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
該示例繪製了 4 個正方形,分別採用四種不同顏色進行填充。這個例子,由於很簡單,就不再像原作者那樣自作多情的分析了。
沒有留言:
張貼留言