LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 2219|回复: 0

怎样用cairo来制作动画,并且不干扰用户交互

[复制链接]
发表于 2009-10-14 16:13:08 | 显示全部楼层 |阅读模式
该文主要参考
http://cairographics.org/threaded_animation_with_cairo/
我做的改进的地方是用了,GThread而不用pthread。
主要的思路是,当显示比较耗费处理器的动画时,可以把动画显示部分放在单独的线程里处理。
假设代码所保存的文件名称为cairoanim.c ,可以用以下命令编译。
gcc -g cairoanim.c -o cairoanim `pkg-config --cflags --libs gtk+-2.0 gthread-2.0`
在windows mingw下编译,测试通过。
  1. #include <gtk/gtk.h>
  2. //the global pixmap that will serve as our buffer
  3. static GdkPixmap *pixmap = NULL;
  4. static guint timer_id = 0;
  5. gboolean on_window_delete( GtkWidget *wgt, GdkEvent  *event, gpointer data)
  6. {
  7.         if( timer_id)
  8.         g_source_remove( timer_id);
  9.         gtk_main_quit();
  10.         return FALSE;
  11. }
  12. gboolean on_window_configure_event(GtkWidget * da, GdkEventConfigure * event, gpointer user_data)
  13. {
  14.         static int oldw = 0;
  15.         static int oldh = 0;
  16.         //make our selves a properly sized pixmap if our window has been resized
  17.         if (oldw != event->width || oldh != event->height){
  18.                 g_object_unref(pixmap);
  19.                 //create our new pixmap with the correct size.
  20.                 pixmap = gdk_pixmap_new(da->window, event->width,  event->height, -1);
  21.         }
  22.         oldw = event->width;
  23.         oldh = event->height;
  24.         return TRUE;
  25. }
  26. gboolean on_window_expose_event(GtkWidget * da, GdkEventExpose * event, gpointer user_data)
  27. {
  28.         gdk_draw_drawable(da->window,
  29.         da->style->fg_gc[GTK_WIDGET_STATE(da)], pixmap,
  30.         // Only copy the area that was exposed.
  31.         event->area.x, event->area.y,
  32.         event->area.x, event->area.y,
  33.         event->area.width, event->area.height);
  34.         return TRUE;
  35. }
  36. static int currently_drawing = 0;
  37. //do_draw will be executed in a separate thread whenever we would like to update
  38. //our animation
  39. void *do_draw(void *ptr)
  40. {
  41.         currently_drawing = 1;
  42.         int width, height;
  43.         gdk_threads_enter();
  44.         gdk_drawable_get_size(pixmap, &width, &height);
  45.         gdk_threads_leave();
  46.         //create a gtk-independant surface to draw on
  47.         cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
  48.         cairo_t *cr = cairo_create(cst);
  49.         //do some time-consuming drawing
  50.         static int i = 0;
  51.         ++i; i = i % 300;   //give a little movement to our animation
  52.         cairo_set_source_rgb (cr, .5, .5, .5);
  53.         cairo_paint(cr);
  54.         int j;
  55.         for(j=0; j < 1000; ++j){
  56.                 cairo_set_source_rgb (cr, (double)j/1000.0, (double)j/1000.0, 1.0 - (double)j/1000.0);
  57.                 cairo_move_to(cr, i,j/2);
  58.                 cairo_line_to(cr, i+100,j/2);
  59.                 cairo_stroke(cr);
  60.         }
  61.         cairo_destroy(cr);
  62.         //When dealing with gdkPixmap's, we need to make sure not to
  63.         //access them from outside gtk_main().
  64.         gdk_threads_enter();
  65.         cairo_t *cr_pixmap = gdk_cairo_create(pixmap);
  66.         cairo_set_source_surface (cr_pixmap, cst, 0, 0);
  67.         cairo_paint(cr_pixmap);
  68.         cairo_destroy(cr_pixmap);
  69.         gdk_threads_leave();
  70.         cairo_surface_destroy(cst);
  71.         currently_drawing = 0;
  72.         return NULL;
  73. }
  74. gboolean timer_exe(GtkWidget * window)
  75. {
  76.         static gboolean first_execution = TRUE;
  77.         //use a safe function to get the value of currently_drawing so
  78.         //we don't run into the usual multithreading issues
  79.         int drawing_status = g_atomic_int_get(&currently_drawing);
  80.         //if we are not currently drawing anything, launch a thread to
  81.         //update our pixmap
  82.         if(drawing_status == 0){
  83.                 static GThread *thread_info = NULL;
  84.                 int  iret;
  85.                 if(first_execution != TRUE){
  86.                         g_thread_join( thread_info);
  87.                 }
  88.                 thread_info = g_thread_create_full( (GThreadFunc)do_draw,
  89.                 NULL,
  90.                 0,
  91.                 TRUE,
  92.                 FALSE,
  93.                 (GThreadPriority)0,
  94.                 NULL);
  95.         }
  96.         //tell our window it is time to draw our animation.
  97.         int width, height;
  98.         gdk_drawable_get_size(pixmap, &width, &height);
  99.         gtk_widget_queue_draw_area(window, 0, 0, width, height);
  100.         first_execution = FALSE;
  101.         return TRUE;
  102. }
  103. int main (int argc, char *argv[])
  104. {
  105.         //we need to initialize all these functions so that gtk knows
  106.         //to be thread-aware
  107.         if (!g_thread_supported ()){ g_thread_init(NULL); }
  108.         gdk_threads_init();
  109.         gdk_threads_enter();
  110.         gtk_init(&argc, &argv);
  111.         GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  112.         g_signal_connect(window, "delete-event", G_CALLBACK(on_window_delete), NULL);
  113.         g_signal_connect(window, "expose_event", G_CALLBACK(on_window_expose_event), NULL);
  114.         g_signal_connect(window, "configure_event", G_CALLBACK(on_window_configure_event), NULL);
  115.         //this must be done before we define our pixmap so that it can reference
  116.         //the colour depth and such
  117.         gtk_widget_show_all(window);
  118.         //set up our pixmap so it is ready for drawing
  119.         pixmap = gdk_pixmap_new(window->window,100,100,-1);
  120.         //because we will be painting our pixmap manually during expose events
  121.         //we can turn off gtk's automatic painting and double buffering routines.
  122.         gtk_widget_set_app_paintable(window, TRUE);
  123.         gtk_widget_set_double_buffered(window, FALSE);
  124.         (void)g_timeout_add(33, (GSourceFunc)timer_exe, window);
  125.         gtk_main();
  126.         gdk_threads_leave();
  127.         return 0;
  128. }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表