Multithreaded rendering with Cairo

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 で試した感じでは、うまくいっている。

コメントを残す