NOTE: if someone need English translation of this entry, I’ll do that with pleasure. If you need, please send me an E-mail.
cairo のドキュメンテーションというのは不思議なほど少なくて、API doc を隅から隅まで読んだ上で、いろいろ試したり想像をふくらませたりして遊んでるわけだが、このたび一念発起して、ずっと開発しているプログラムの、cairo を使ったレンダリングを並列化することにした。自分が dual core なプロセッサのマシンを使っているのに、書いているプログラムが single threaded だなんて、かっこ悪いからな。
基本的なやり方としては、メインの画像の一部を別のスレッドでレンダリングしてはめ込む、もしくは alpha channel を使って上に重ねるわけだが、どちらもやり方はほとんど同じだと思う。まず最初に、スレッドの数だけの surface と cairo drawing context を作る。このとき、surface type はメインの画像のものと同じにするが、surface content は color with alpha にしておくのが大事だと思う。
メインの cairo_t を cr とすると、
cairo_surface_t* surface; cairo_surface_t* surface_p[10]; cairo_t* cr_p[10]; // create surfaces surface = cairo_get_target(cr); for(int i=0; i<10; i++){ surface_p[i] = cairo_surface_create_similar(surface, CAIRO_CONTENT_COLOR_ALPHA, WIDTH, HEIGHT); cr_p[i] = cairo_create(surface_p[i]); }
こんな感じで、surface と cairo context をたくさん作って、あとはそれぞれの context の上にじゃんじゃか絵を描けばよい。
さて、問題はこれをはめ込むときだ。仕掛けとしては、さきほどたくさん作った surface から cairo_pattern_create_for_surface() で pattern を作り、それを cairo_set_source() で指定した上で cairo_rectangle(); cairo_fill() とかすればよいのだが、重要なのは、cairo_pattern を使う場合、pattern は rectangle などの位置に関係なく、必ず(0, 0) を基準にして展開される ということである。したがって、rectangle の位置に pattern の位置が合うように、transformation matrix を指定してやらねばならない。
cairo_pattern_t* pattern; for(int i=0; i<10; i++){ cairo_matrix_t m; pattern = cairo_pattern_create_for_surface(surface_p[i]); cairo_matrix_init_identity(&m); m.x0 = -XPOS; // -XPOS, but not XPOS ! m.y0 = -YPOS; // -YPOS, but not YPOS! cairo_pattern_set_matrix(pattern, &m); cairo_set_source(cr, pattern); cairo_rectangle(cr, XPOS, YPOS, WIDTH, HEIGHT); cairo_fill(cr); cairo_pattern_destroy(pattern); cairo_surface_destroy(surface_p[i]); cairo_destroy(cr_p[i]); }
とまあ、こんな感じ。GTK とか PDF で試した感じでは、うまくいっている。