关闭

Cairo 图形指南 (3) —— 变换

176人阅读 评论(0) 收藏 举报

这一篇讲述变换(Transformation) 仿射变换是由一些线性变换与平移构成的。线性变换可以写为单个矩阵的形式。旋转是让一个刚体绕一点运动的变换。缩放变换是让物体的形状扩大与减小,并且在各个方向上的缩放因子都相同。平移变换将每个点沿着指定的方向移动常量距离。错切对于给定轴线,沿垂直于它的方向对物体进行移动的变换,并且在轴线的一侧的移动距离大于另一侧。

——上述内容来自维基百科全书

平移

下面这个例子演示了一个简单的平移变换。

  1. #include <cairo.h>  
  2. #include <gtk/gtk.h>  
  3.    
  4.    
  5. staticgboolean  
  6. on_expose_event(GtkWidget *widget,  
  7.     GdkEventExpose *event,  
  8.     gpointer data)  
  9. {  
  10.   cairo_t *cr;  
  11.    
  12.   cr = gdk_cairo_create (widget->window);  
  13.    
  14.   cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  
  15.   cairo_rectangle(cr, 20, 20, 80, 50);  
  16.   cairo_stroke_preserve(cr);  
  17.   cairo_set_source_rgb(cr, 1, 1, 1);  
  18.   cairo_fill(cr);  
  19.    
  20.   cairo_translate(cr, 100, 100);  
  21.    
  22.   cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  
  23.   cairo_rectangle(cr, 20, 20, 80, 50);  
  24.   cairo_stroke_preserve(cr);  
  25.   cairo_set_source_rgb(cr, 1, 1, 1);  
  26.   cairo_fill(cr);  
  27.    
  28.   cairo_destroy(cr);  
  29.    
  30.   returnFALSE;  
  31. }  
  32.    
  33.    
  34. int main(int argc,char *argv[])  
  35. {  
  36.   GtkWidget *window;  
  37.    
  38.   gtk_init(&argc, &argv);  
  39.    
  40.   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);  
  41.    
  42.   g_signal_connect(window,"expose-event",  
  43.       G_CALLBACK (on_expose_event), NULL);  
  44.   g_signal_connect(window,"destroy",  
  45.       G_CALLBACK (gtk_main_quit), NULL);  
  46.    
  47.   gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);  
  48.   gtk_window_set_default_size(GTK_WINDOW(window), 300, 230);  
  49.   gtk_widget_set_app_paintable(window, TRUE);  
  50.    
  51.   gtk_widget_show_all(window);  
  52.    
  53.   gtk_main();  
  54.    
  55.   return0;  
  56. }  



这个例子先是画了个矩形,然后将它平移并绘制出平移结果。


cairo_translate(cr, 100, 100);

cairo_translate() 函数可通过平移用于空间的原点来修改当前的变换矩阵。在这个示例中,是将原点沿水平和竖直方向平移了 100 个单位长度。

旋转

下面这个例子演示了一个简单的旋转变换。


  1. #include <cairo.h>  
  2. #include <gtk/gtk.h>  
  3. #include <math.h>  
  4.    
  5.    
  6. staticgboolean  
  7. on_expose_event(GtkWidget *widget,  
  8.     GdkEventExpose *event,  
  9.     gpointer data)  
  10. {  
  11.   cairo_t *cr;  
  12.    
  13.   cr = gdk_cairo_create (widget->window);  
  14.    
  15.   cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  
  16.   cairo_rectangle(cr, 20, 20, 80, 50);  
  17.   cairo_stroke_preserve(cr);  
  18.   cairo_set_source_rgb(cr, 1, 1, 1);  
  19.   cairo_fill(cr);  
  20.    
  21.   cairo_translate(cr, 150, 100);  
  22.   cairo_rotate(cr, M_PI/2);  
  23.    
  24.   cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  
  25.   cairo_rectangle(cr, 20, 20, 80, 50);  
  26.   cairo_stroke_preserve(cr);  
  27.   cairo_set_source_rgb(cr, 1, 1, 1);  
  28.   cairo_fill(cr);  
  29.    
  30.   cairo_destroy(cr);  
  31.    
  32.   returnFALSE;  
  33. }  
  34.    
  35.    
  36. int main(int argc,char *argv[])  
  37. {  
  38.   GtkWidget *window;  
  39.    
  40.   gtk_init(&argc, &argv);  
  41.    
  42.   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);  
  43.    
  44.   g_signal_connect(window,"expose-event",  
  45.       G_CALLBACK (on_expose_event), NULL);  
  46.   g_signal_connect(window,"destroy",  
  47.       G_CALLBACK (gtk_main_quit), NULL);  
  48.    
  49.   gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);  
  50.   gtk_window_set_default_size(GTK_WINDOW(window), 300, 230);  
  51.   gtk_widget_set_app_paintable(window, TRUE);  
  52.    
  53.   gtk_widget_show_all(window);  
  54.    
  55.   gtk_main();  
  56.    
  57.   return0;  
  58. }  


这个例子先是画了个矩形,然后对它进行了平移和旋转变换,并绘制出变换结果。


cairo_translate(cr, 150, 100);
cairo_rotate(cr, M_PI/2);

首先对用户空间的原点进行平移,然后再围绕它旋转 180°。注意:旋转角度是弧度,而非角度。

缩放

下面这个例子演示了一个对象的缩放变换。(作者还真是沉闷阿,相同的句式连用了 n 次,这个可怜的矩形被折腾的痛苦不堪!)


  1. #include <cairo.h>  
  2. #include <gtk/gtk.h>  
  3.    
  4.    
  5. staticgboolean  
  6. on_expose_event(GtkWidget *widget,  
  7.     GdkEventExpose *event,  
  8.     gpointer data)  
  9. {  
  10.   cairo_t *cr;  
  11.    
  12.   cr = gdk_cairo_create (widget->window);  
  13.    
  14.   cairo_save(cr);  
  15.   cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  
  16.   cairo_rectangle(cr, 20, 30, 80, 50);  
  17.   cairo_stroke_preserve(cr);  
  18.   cairo_set_source_rgb(cr, 1, 1, 1);  
  19.   cairo_fill(cr);  
  20.   cairo_restore(cr);  
  21.    
  22.   cairo_save(cr);  
  23.   cairo_translate(cr, 130, 30);  
  24.   cairo_scale(cr, 0.7, 0.7);  
  25.    
  26.   cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  
  27.   cairo_rectangle(cr, 0, 0, 80, 50);  
  28.   cairo_stroke_preserve(cr);  
  29.   cairo_set_source_rgb(cr, 1, 1, 1);  
  30.   cairo_fill(cr);  
  31.   cairo_restore(cr);  
  32.    
  33.   cairo_save(cr);  
  34.   cairo_translate(cr, 220, 30);  
  35.   cairo_scale(cr, 1.5, 1.5);  
  36.    
  37.   cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  
  38.   cairo_rectangle(cr, 0, 0, 80, 50);  
  39.   cairo_stroke_preserve(cr);  
  40.   cairo_set_source_rgb(cr, 1, 1, 1);  
  41.   cairo_fill(cr);  
  42.   cairo_restore(cr);  
  43.    
  44.   cairo_destroy(cr);  
  45.    
  46.   returnFALSE;  
  47. }  
  48.    
  49.    
  50. int main(int argc,char *argv[])  
  51. {  
  52.   GtkWidget *window;  
  53.    
  54.   gtk_init(&argc, &argv);  
  55.    
  56.   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);  
  57.    
  58.   g_signal_connect(window,"expose-event",  
  59.       G_CALLBACK (on_expose_event), NULL);  
  60.   g_signal_connect(window,"destroy",  
  61.       G_CALLBACK (gtk_main_quit), NULL);  
  62.    
  63.   gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);  
  64.   gtk_window_set_default_size(GTK_WINDOW(window), 360, 140);  
  65.   gtk_widget_set_app_paintable(window, TRUE);  
  66.    
  67.   gtk_widget_show_all(window);  
  68.    
  69.   gtk_main();  
  70.    
  71.   return0;  
  72. }  


这次的例子是用指定的缩放因子,把初始的矩形变的小了点,然后又把它变的大了点。


cairo_save(cr);
...
cairo_restore(cr);

若对初始的矩形完成两次缩放操作,需要将初始的变换矩阵保存一下,这个可通过 cairo_save() 和 cairo_restore() 函数来实现。


cairo_translate(cr, 130, 30);
cairo_scale(cr, 0.7, 0.7);

这里首先将用户空间的原点平移了一下,然后又开始用 0.7 作为因子进行缩放变换。

 

错切

在下面的示例中,我们来实现错切变换。


  1. #include <cairo.h>  
  2. #include <gtk/gtk.h>  
  3.    
  4.    
  5. staticgboolean  
  6. on_expose_event(GtkWidget *widget,  
  7.     GdkEventExpose *event,  
  8.     gpointer data)  
  9. {  
  10.   cairo_t *cr;  
  11.   cairo_matrix_t matrix;  
  12.    
  13.   cr = gdk_cairo_create (widget->window);  
  14.    
  15.   cairo_save(cr);  
  16.   cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  
  17.   cairo_rectangle(cr, 20, 30, 80, 50);  
  18.   cairo_stroke_preserve(cr);  
  19.   cairo_set_source_rgb(cr, 1, 1, 1);  
  20.   cairo_fill(cr);  
  21.   cairo_restore(cr);  
  22.    
  23.   cairo_save(cr);  
  24.   cairo_translate(cr, 130, 30);  
  25.   cairo_matrix_init(&matrix,  
  26.       1.0, 0.5,  
  27.       0.0, 1.0,  
  28.       0.0, 0.0);  
  29.    
  30.   cairo_transform (cr, &matrix);  
  31.    
  32.   cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  
  33.   cairo_rectangle(cr, 0, 0, 80, 50);  
  34.   cairo_stroke_preserve(cr);  
  35.   cairo_set_source_rgb(cr, 1, 1, 1);  
  36.   cairo_fill(cr);  
  37.   cairo_restore(cr);  
  38.    
  39.   cairo_save(cr);  
  40.   cairo_translate(cr, 220, 30);  
  41.   cairo_matrix_init(&matrix,  
  42.       1.0, 0.0,  
  43.       0.7, 1.0,  
  44.       0.0, 0.0);  
  45.    
  46.   cairo_transform(cr, &matrix);  
  47.    
  48.   cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);  
  49.   cairo_rectangle(cr, 0, 0, 80, 50);  
  50.   cairo_stroke_preserve(cr);  
  51.   cairo_set_source_rgb(cr, 1, 1, 1);  
  52.   cairo_fill(cr);  
  53.   cairo_restore(cr);  
  54.    
  55.   cairo_destroy(cr);  
  56.    
  57.   returnFALSE;  
  58. }  
  59.    
  60.    
  61. int main(int argc,char *argv[])  
  62. {  
  63.   GtkWidget *window;  
  64.    
  65.   gtk_init(&argc, &argv);  
  66.    
  67.   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);  
  68.    
  69.   g_signal_connect(window,"expose-event",  
  70.       G_CALLBACK(on_expose_event), NULL);  
  71.   g_signal_connect(window,"destroy",  
  72.       G_CALLBACK(gtk_main_quit), NULL);  
  73.    
  74.   gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);  
  75.   gtk_window_set_default_size(GTK_WINDOW(window), 360, 140);  
  76.   gtk_widget_set_app_paintable(window, TRUE);  
  77.    
  78.   gtk_widget_show_all(window);  
  79.    
  80.   gtk_main();  
  81.    
  82.   return0;  
  83. }  


这份示例代码实现了两次错切变换。对于错切变换,没有特定的函数,必须使用矩阵来实现。


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);

 

这一变换的数学形式可表示为:

 

椭圆

下面的这个例子,画了一个灰常复杂的形状,它由一串旋转的椭圆形成。


  1. #include <cairo.h>  
  2. #include <gtk/gtk.h>  
  3.    
  4. staticgboolean  
  5. on_expose_event(GtkWidget *widget,  
  6.     GdkEventExpose *event,  
  7.     gpointer data)  
  8. {  
  9.   cairo_t *cr;  
  10.    
  11.   cr = gdk_cairo_create(widget->window);  
  12.    
  13.   gint width, height;  
  14.   gtk_window_get_size(GTK_WINDOW(widget), &width, &height);  
  15.    
  16.   cairo_set_line_width(cr, 0.5);  
  17.   cairo_translate(cr, width/2, height/2);  
  18.   cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI);  
  19.   cairo_stroke(cr);  
  20.    
  21.   gint i;  
  22.    
  23.   cairo_save(cr);  
  24.   for( i = 0; i < 36; i++) {  
  25.       cairo_rotate(cr, i*M_PI/36);  
  26.       cairo_scale(cr, 0.3, 1);  
  27.       cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI);  
  28.       cairo_restore(cr);  
  29.       cairo_stroke(cr);  
  30.       cairo_save(cr);  
  31.   }  
  32.    
  33.   cairo_destroy(cr);  
  34.    
  35.   returnFALSE;  
  36. }  
  37.    
  38.    
  39. int main(int argc,char *argv[])  
  40. {  
  41.    
  42.   GtkWidget *window;  
  43.    
  44.   gtk_init(&argc, &argv);  
  45.    
  46.   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);  
  47.    
  48.   g_signal_connect(G_OBJECT(window),"expose-event",  
  49.       G_CALLBACK(on_expose_event), NULL);  
  50.   g_signal_connect(G_OBJECT(window),"destroy",  
  51.       G_CALLBACK(gtk_main_quit), NULL);  
  52.    
  53.   gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);  
  54.   gtk_window_set_default_size(GTK_WINDOW(window), 350, 250);  
  55.    
  56.   gtk_widget_set_app_paintable(window, TRUE);  
  57.   gtk_widget_show_all(window);  
  58.    
  59.   gtk_main();  
  60.    
  61.   return0;  
  62. }  



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 个椭圆。椭圆可用圆的缩放变换而获得。旋转这个椭圆,这样就创建了一个有趣的形状。

 

星星

下面的示例绘制了一个又旋转又缩放的星星,可惜不会发光呃。


  1. #include <cairo.h>  
  2. #include <gtk/gtk.h>  
  3. #include <math.h>  
  4.    
  5. int points[11][2] = {  
  6.     { 0, 85 },  
  7.     { 75, 75 },  
  8.     { 100, 10 },  
  9.     { 125, 75 },  
  10.     { 200, 85 },  
  11.     { 150, 125 },  
  12.     { 160, 190 },  
  13.     { 100, 150 },  
  14.     { 40, 190 },  
  15.     { 50, 125 },  
  16.     { 0, 85 }  
  17. };  
  18.    
  19.    
  20. staticgboolean  
  21. on_expose_event(GtkWidget *widget,  
  22.     GdkEventExpose *event,  
  23.     gpointer data)  
  24. {  
  25.   cairo_t *cr;  
  26.    
  27.   staticgdouble angle = 0;  
  28.   staticgdouble scale = 1;  
  29.   staticgdouble delta = 0.01;  
  30.    
  31.   gint width, height;  
  32.   gtk_window_get_size(GTK_WINDOW(widget), &width, &height);  
  33.    
  34.   cr = gdk_cairo_create(widget->window);  
  35.    
  36.   cairo_set_source_rgb(cr, 0, 0.44, 0.7);  
  37.   cairo_set_line_width(cr, 1);  
  38.    
  39.   cairo_translate(cr, width / 2, height / 2 );  
  40.   cairo_rotate(cr, angle);  
  41.   cairo_scale(cr, scale, scale);  
  42.    
  43.   gint i;  
  44.    
  45.   for( i = 0; i < 10; i++ ) {  
  46.       cairo_line_to(cr, points[i][0], points[i][1]);  
  47.   }  
  48.    
  49.   cairo_close_path(cr);  
  50.   cairo_fill(cr);  
  51.   cairo_stroke(cr);  
  52.    
  53.   if( scale < 0.01 ) {  
  54.       delta = -delta;  
  55.   }else if (scale > 0.99) {  
  56.       delta = -delta;  
  57.   }  
  58.    
  59.   scale += delta;  
  60.   angle += 0.01;  
  61.    
  62.   cairo_destroy(cr);  
  63.    
  64.   returnFALSE;  
  65. }  
  66.    
  67. staticgboolean  
  68. time_handler (GtkWidget *widget)  
  69. {  
  70.   if(widget->window == NULL) returnFALSE;  
  71.   gtk_widget_queue_draw(widget);  
  72.   returnTRUE;  
  73. }  
  74.    
  75.    
  76. int main(int argc,char *argv[])  
  77. {  
  78.    
  79.   GtkWidget *window;  
  80.    
  81.   gtk_init(&argc, &argv);  
  82.    
  83.   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);  
  84.    
  85.   gtk_widget_add_events (window, GDK_BUTTON_PRESS_MASK);  
  86.    
  87.   g_signal_connect(window,"expose-event",  
  88.       G_CALLBACK(on_expose_event), NULL);  
  89.   g_signal_connect(window,"destroy",  
  90.       G_CALLBACK(gtk_main_quit), NULL);  
  91.    
  92.     
  93.   gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);  
  94.   gtk_window_set_title(GTK_WINDOW(window),"star");  
  95.   gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);  
  96.   gtk_widget_set_app_paintable(window, TRUE);  
  97.    
  98.   g_timeout_add(10, (GSourceFunc) time_handler, (gpointer) window);   
  99.    
  100.   gtk_widget_show_all(window);  
  101.    
  102.   gtk_main();  
  103.    
  104.   return0;  
  105. }  


在这个示例中,画了一颗星星,然后平移它,旋转它,缩放它。


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;
} elseif(scale > 0.99) {
    delta = -delta;
}

这几行代码控制星星的缩放过程。





转自    http://blog.csdn.net/haiwil/article/details/6771768


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:20169次
    • 积分:285
    • 等级:
    • 排名:千里之外
    • 原创:3篇
    • 转载:42篇
    • 译文:0篇
    • 评论:0条
    文章分类