這一篇講述變換(Transformation) 仿射變換是由一些線性變換與平移構成的。線性變換可以寫爲單個矩陣的形式。旋轉是讓一個剛體繞一點運動的變換。縮放變換是讓物體的形狀擴大與減小,並且在各個方向上的縮放因子都相同。平移變換將每個點沿着指定的方向移動常量距離。錯切對於給定軸線,沿垂直於它的方向對物體進行移動的變換,並且在軸線的一側的移動距離大於另一側。
——上述內容來自維基百科全書
平移
下面這個例子演示了一個簡單的平移變換。
這個例子先是畫了個矩形,然後將它平移並繪製出平移結果。
cairo_translate(cr, 100, 100); |
cairo_translate() 函數可通過平移用於空間的原點來修改當前的變換矩陣。在這個示例中,是將原點沿水平和豎直方向平移了 100 個單位長度。
旋轉
下面這個例子演示了一個簡單的旋轉變換。
#include <cairo.h>
#include <gtk/gtk.h>
#include <math.h>
staticgboolean
on_expose_event(GtkWidget *widget,
GdkEventExpose *event,
gpointer data)
{
cairo_t *cr;
cr = gdk_cairo_create (widget->window);
cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
cairo_rectangle(cr, 20, 20, 80, 50);
cairo_stroke_preserve(cr);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_fill(cr);
cairo_translate(cr, 150, 100);
cairo_rotate(cr, M_PI/2);
cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
cairo_rectangle(cr, 20, 20, 80, 50);
cairo_stroke_preserve(cr);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_fill(cr);
cairo_destroy(cr);
returnFALSE;
}
int main(int argc,char *argv[])
{
GtkWidget *window;
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);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 230);
gtk_widget_set_app_paintable(window, TRUE);
gtk_widget_show_all(window);
gtk_main();
return0;
}
這個例子先是畫了個矩形,然後對它進行了平移和旋轉變換,並繪製出變換結果。
cairo_translate(cr, 150, 100); cairo_rotate(cr, M_PI/2); |
首先對用戶空間的原點進行平移,然後再圍繞它旋轉 180°。注意:旋轉角度是弧度,而非角度。
縮放
下面這個例子演示了一個對象的縮放變換。(作者還真是沉悶阿,相同的句式連用了 n 次,這個可憐的矩形被折騰的痛苦不堪!)
#include <cairo.h>
#include <gtk/gtk.h>
staticgboolean
on_expose_event(GtkWidget *widget,
GdkEventExpose *event,
gpointer data)
{
cairo_t *cr;
cr = gdk_cairo_create (widget->window);
cairo_save(cr);
cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
cairo_rectangle(cr, 20, 30, 80, 50);
cairo_stroke_preserve(cr);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_fill(cr);
cairo_restore(cr);
cairo_save(cr);
cairo_translate(cr, 130, 30);
cairo_scale(cr, 0.7, 0.7);
cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
cairo_rectangle(cr, 0, 0, 80, 50);
cairo_stroke_preserve(cr);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_fill(cr);
cairo_restore(cr);
cairo_save(cr);
cairo_translate(cr, 220, 30);
cairo_scale(cr, 1.5, 1.5);
cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
cairo_rectangle(cr, 0, 0, 80, 50);
cairo_stroke_preserve(cr);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_fill(cr);
cairo_restore(cr);
cairo_destroy(cr);
returnFALSE;
}
int main(int argc,char *argv[])
{
GtkWidget *window;
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);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 360, 140);
gtk_widget_set_app_paintable(window, TRUE);
gtk_widget_show_all(window);
gtk_main();
return0;
}
這次的例子是用指定的縮放因子,把初始的矩形變的小了點,然後又把它變的大了點。
cairo_save(cr); ... cairo_restore(cr); |
若對初始的矩形完成兩次縮放操作,需要將初始的變換矩陣保存一下,這個可通過 cairo_save() 和 cairo_restore() 函數來實現。
cairo_translate(cr, 130, 30); cairo_scale(cr, 0.7, 0.7); |
這裏首先將用戶空間的原點平移了一下,然後又開始用 0.7 作爲因子進行縮放變換。
錯切
在下面的示例中,我們來實現錯切變換。
#include <cairo.h>
#include <gtk/gtk.h>
staticgboolean
on_expose_event(GtkWidget *widget,
GdkEventExpose *event,
gpointer data)
{
cairo_t *cr;
cairo_matrix_t matrix;
cr = gdk_cairo_create (widget->window);
cairo_save(cr);
cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
cairo_rectangle(cr, 20, 30, 80, 50);
cairo_stroke_preserve(cr);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_fill(cr);
cairo_restore(cr);
cairo_save(cr);
cairo_translate(cr, 130, 30);
cairo_matrix_init(&matrix,
1.0, 0.5,
0.0, 1.0,
0.0, 0.0);
cairo_transform (cr, &matrix);
cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
cairo_rectangle(cr, 0, 0, 80, 50);
cairo_stroke_preserve(cr);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_fill(cr);
cairo_restore(cr);
cairo_save(cr);
cairo_translate(cr, 220, 30);
cairo_matrix_init(&matrix,
1.0, 0.0,
0.7, 1.0,
0.0, 0.0);
cairo_transform(cr, &matrix);
cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
cairo_rectangle(cr, 0, 0, 80, 50);
cairo_stroke_preserve(cr);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_fill(cr);
cairo_restore(cr);
cairo_destroy(cr);
returnFALSE;
}
int main(int argc,char *argv[])
{
GtkWidget *window;
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);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 360, 140);
gtk_widget_set_app_paintable(window, TRUE);
gtk_widget_show_all(window);
gtk_main();
return0;
}
這份示例代碼實現了兩次錯切變換。對於錯切變換,沒有特定的函數,必須使用矩陣來實現。
cairo_matrix_t matrix; |
這個 cairo_matrix 是存儲仿射變換的數據結構。
cairo_matrix_init(&matrix, 1.0, 0.5, 0.0, 1.0, 0.0, 0.0); cairo_transform (cr, &matrix); |
這一變換的數學形式可表示爲:
cairo_matrix_init(&matrix, 1.0, 0.0, 0.7, 1.0, 0.0, 0.0); cairo_transform(cr, &matrix); |
這一變換的數學形式可表示爲:
橢圓
下面的這個例子,畫了一個灰常複雜的形狀,它由一串旋轉的橢圓形成。
#include <cairo.h>
#include <gtk/gtk.h>
staticgboolean
on_expose_event(GtkWidget *widget,
GdkEventExpose *event,
gpointer data)
{
cairo_t *cr;
cr = gdk_cairo_create(widget->window);
gint width, height;
gtk_window_get_size(GTK_WINDOW(widget), &width, &height);
cairo_set_line_width(cr, 0.5);
cairo_translate(cr, width/2, height/2);
cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI);
cairo_stroke(cr);
gint i;
cairo_save(cr);
for( i = 0; i < 36; i++) {
cairo_rotate(cr, i*M_PI/36);
cairo_scale(cr, 0.3, 1);
cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI);
cairo_restore(cr);
cairo_stroke(cr);
cairo_save(cr);
}
cairo_destroy(cr);
returnFALSE;
}
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), 350, 250);
gtk_widget_set_app_paintable(window, TRUE);
gtk_widget_show_all(window);
gtk_main();
return0;
}
cairo_translate(cr, width/2, height/2); cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI); cairo_stroke(cr); |
在 GTK+ 的窗口中間,繪製了一個圓,它是那些橢圓的邊界圓。
cairo_save(cr); for
( i = 0; i < 36; i++) { cairo_rotate(cr, i*M_PI/36); cairo_scale(cr, 0.3, 1); cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI); cairo_restore(cr); cairo_stroke(cr); cairo_save(cr); } |
沿着邊界圓畫 36 個橢圓。橢圓可用圓的縮放變換而獲得。旋轉這個橢圓,這樣就創建了一個有趣的形狀。
星星
下面的示例繪製了一個又旋轉又縮放的星星,可惜不會發光呃。
#include <cairo.h>
#include <gtk/gtk.h>
#include <math.h>
int points[11][2] = {
{ 0, 85 },
{ 75, 75 },
{ 100, 10 },
{ 125, 75 },
{ 200, 85 },
{ 150, 125 },
{ 160, 190 },
{ 100, 150 },
{ 40, 190 },
{ 50, 125 },
{ 0, 85 }
};
staticgboolean
on_expose_event(GtkWidget *widget,
GdkEventExpose *event,
gpointer data)
{
cairo_t *cr;
staticgdouble angle = 0;
staticgdouble scale = 1;
staticgdouble delta = 0.01;
gint width, height;
gtk_window_get_size(GTK_WINDOW(widget), &width, &height);
cr = gdk_cairo_create(widget->window);
cairo_set_source_rgb(cr, 0, 0.44, 0.7);
cairo_set_line_width(cr, 1);
cairo_translate(cr, width / 2, height / 2 );
cairo_rotate(cr, angle);
cairo_scale(cr, scale, scale);
gint i;
for( i = 0; i < 10; i++ ) {
cairo_line_to(cr, points[i][0], points[i][1]);
}
cairo_close_path(cr);
cairo_fill(cr);
cairo_stroke(cr);
if( scale < 0.01 ) {
delta = -delta;
}else if (scale > 0.99) {
delta = -delta;
}
scale += delta;
angle += 0.01;
cairo_destroy(cr);
returnFALSE;
}
staticgboolean
time_handler (GtkWidget *widget)
{
if(widget->window == NULL) returnFALSE;
gtk_widget_queue_draw(widget);
returnTRUE;
}
int main(int argc,char *argv[])
{
GtkWidget *window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_add_events (window, GDK_BUTTON_PRESS_MASK);
g_signal_connect(window,"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_title(GTK_WINDOW(window),"star");
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
gtk_widget_set_app_paintable(window, TRUE);
g_timeout_add(10, (GSourceFunc) time_handler, (gpointer) window);
gtk_widget_show_all(window);
gtk_main();
return0;
}
在這個示例中,畫了一顆星星,然後平移它,旋轉它,縮放它。
cairo_translate(cr, width / 2, height / 2 ); cairo_rotate(cr, angle); cairo_scale(cr, scale, scale |
先將星星平移到窗口中間,旋轉它,縮放它。(作者還真不是一般的羅嗦)
for
( i = 0; i < 10; i++ ) { cairo_line_to(cr, points[i][0], points[i][1]); } cairo_close_path(cr); cairo_fill(cr); cairo_stroke(cr); |
畫它!
if
( scale < 0.01 ) { delta = -delta; } else if
(scale > 0.99) { delta = -delta; } |
這幾行代碼控制星星的縮放過程。