2009年07月05日

オープンソースのSVGライブラリ

前回、Blenderにvectexの機能を組み込んで、ようやくvectexの機能自体を改造する準備ができました。
ここで、使用するSVGファイルを指定する際にファイルセレクタを使用するようにしたり、UVマッピング以外のマッピングが使えない制限を改善したりと色々やりたいことがあるのですが、なによりもまず普通のSVGファイルならどんなものでも使用できるようにSVG描画機能そのものを改善したいと思います。

現在のvectexではフォントを使った文字、グラデーションやぼかしなど色々とSVGの仕様に対応していないため、あらかじめ文字をパスに変換しておくなどの対応をしておく必要があります。
これではかなり使いにくいので、できればInkscapeなどを使って作成したイラストのデータをそのまま利用できるようにしたいところです。

そこで次の目標として、vectexのSVG描画エンジンをAGG、Expatを使ったものからオープンソースの別のSVGライブラリに変更する、という改造を進めていきたいと思います。

使用するSVGライブラリには、
・vectexが使用しているメモリ上の画像領域にSVG画像を直接描画できる
・SVG画像の描画の際にオフセット、スケーリングをAGGと同じように変換行列で指定できる
というようなことが必要になりそうです。

調べてみたところ、オープンソースのSVGライブラリとしては、
・GNOMEプロジェクトのlibrsvg
・KDEのKSVG(KHTML)
・QtのQtSVG
・WXWindowのwxSVG
などが見つかりました。
これ以外に、Inkscapeの内部機能を調べて描画する部分だけを使わせてもらうということも可能ではあると思いますが、他のプログラムから利用されることを前提にして作られているライブラリと比べると、色々と大変そうです。

私自身が普段GNOME DESKTOPを使用していることもあり、とりあえずlibrsvgを第一候補として選びました。

○librsvg
調べてみると、librsvgはCairoという2Dグラフィックスライブラリと組み合わせて使うことで、vectexがAGGを使っているのと同じように独自に確保したメモリ領域を使用することができるようです。
また、Cairoの機能として、描画領域のオフセットやスケーリングを指定するような変換行列を使うこともできるようです。

librsvgをvectexに組み込む前に、テストとして簡単なサンプルプログラムを作ってみます。
SVGファイルを開いて、PNGファイルとして保存するというものです。

vectexのコードとして使用することを考えて、mallocを使って自前で確保した画像用メモリを使用したり、変換行列を使ってスケールの変更をしたりしています。

librsvg_test.c
#include <librsvg/rsvg.h>
#include <librsvg/rsvg-cairo.h>
#include <cairo-svg.h>
#include <stdio.h>
#include <stdlib.h>

int
main (int argc, char *argv[])
{
    cairo_t *cr3;
    cairo_surface_t *image;
   
    RsvgHandle *r_svg;
    RsvgError rs_err;
    RsvgDimensionData svgdim;
   
    cairo_matrix_t matrix;
   
    int width = 256;
    int height = 256;
   
    unsigned char          *buf = (unsigned char *)malloc(width * height * 4);
   
    rsvg_init();
    r_svg = rsvg_handle_new_from_file("/home/mato/Desktop/sample/tiger.svg", NULL);
    rsvg_handle_get_dimensions(r_svg,&svgdim);

    image = cairo_image_surface_create_for_data(buf, CAIRO_FORMAT_ARGB32, width, height, width * 4);
    cr3 = cairo_create (image);
   
    cairo_set_source_rgb (cr3, 1.0, 0.5, 0.0);
    cairo_paint (cr3);
   
    cairo_matrix_init_identity(&matrix);
    cairo_matrix_scale(&matrix, 0.5, 0.5);
    cairo_transform(cr3, &matrix);
   
    rsvg_handle_render_cairo(r_svg, cr3);
   
    cairo_surface_write_to_png (image, "hello.png");

    rsvg_handle_free(r_svg);
    rsvg_term();

    cairo_destroy (cr3);
    cairo_surface_destroy (image);

    free(buf);
   
    return 0;
}

SConscript
env = Environment()
env.MergeFlags('!pkg-config --cflags --libs librsvg-2.0')
env.Program('librsvg_test.c')

このサンプルプログラムをコンパイルするためのScons用のSConscriptファイルでは、librsvgのコンパイルオプションを指定するためにpkg-configという機能を使っています。
librsvgはライブラリの依存関係がかなり面倒なのですが、このツールを使うとMakefileやSConsvriptがかなりすっきりとしたものになります。

実際にコンパイルするときには必要な様々なライブラリのインクルードファイルの場所、ライブラリの場所、ライブラリ名などが展開されます。

Blenderにライブラリを組み込む際には、このサンプルファイルのコンパイル時に表示されるメッセージやライブラリ自体のMakefileなどを参考にしてSconsの設定ファイルを修正していくことになります。

ちなみに、このサンプルプログラムはソースコード中でSVGファイルの名前を指定していますので、実際にコンパイル、実行する場合にはこの部分を適当に書き換える必要があります。
また、librsvgは当然として、それ以外にもcairo、GLIB、GKT+などいくつかのライブラリの開発用パッケージをインストールしておく必要があります。

サンプルプログラムが正常に動作することが確認できて、librsvgがvectexのバックエンドとして使えそうな感触をつかむことができました。
pic090704_01.jpg

さっそく、Blenderに組み込んでみました...が、色々あってこのlibrsvgを使ってvectexを改造する計画は保留することにしました。
実は、librsvgは絵の再現性については文句なしなのですが、vectexで使うにはかなり致命的な問題があることに、後から気が付きました。

一応、AGG、Expatを置き換える形でlibrsvgをBlenderに組み込んで動作させるところまで改造を進めてあります。
現状でも、元のvectexでは読み込めなかったテキストやグラデーションなどを使ったSVGファイルを読み込んで、Blenderのレンダリング画像の中に表示させることができるようになっています。
pic090704_07.jpg

問題というのは、SVG画像の細部をある程度以上に拡大する状況で、拡大率に応じてテクスチャの作成に時間がかかるようになり、ある限界以上の解像度になるとプログラムが停止してしまうこともあるというものです。
レンダリングのテストでAGG、Expatのバージョンで1分以内にレンダリングできたものが、librsvgを使った場合20分以上かかるということもありました。

また、ライブラリの依存関係が複雑なこともあって、Blenderのビルドシステムへの組み込みが今ひとつうまくいっていないらしく、動作が非常に不安定な感じになっています。

AGG、Expatを使った元のvectexでは、SVG画像の一部を拡大表示する際に、どこをどれだけ拡大しているかという描画領域の違いによって、描画時間が極端に長くなるようなことは感じられません。
しかし、librsvgのサンプルSVGビュワーやMozilla FirefoxなどでSVGファイルを表示して、画像の一部をどんどんと拡大していくと、拡大率が増えるにしたがって処理速度が低下し、ある限界を超えるとSVGビュワーでは描画不能となり、Firefoxでは拡大ができなくなります。
pic090704_02.jpg pic090704_03.jpg

Adobeの作成したInternet Explore用のSVGプラグインでも同じようなことが起こるようですので、これはlibrsvgに何か問題があるということではなくて、描画品質を最大限に高めるようなSVG描画エンジンを目指すと、こういう形にならざるを得ないという仕様のようなものなのだと思います。

結局、このような仕様のライブラリは、vectexに組み込んでレンダリングの状況に応じて最適な解像度のテクスチャを作成するという用途には、そもそも初めから向いていなかったということが分かりました。

○QtSVG
ということで、次の候補を探しているうちに、QtSVGというものがあることを知りました。
QtというのはLinuxのデスクトップ環境KDEで使われているプログラミングツールキットです。
このQtのサンプルの中にSVGビュワーが含まれています。Qt4のExampleの中のPaintingというカテゴリに入っています。
pic090704_04.jpg

このサンプルプログラムを動かしてみて、拡大表示の動作が非常に高速なことに驚きました。
起動時に既定で読み込まれるSVGファイルがいきなりアニメーションファイルになっています。
そして、Renderingメニューを開くとOpenGLという項目があって、2D描画を行う際にOpenGLを使ったハードウェアレンダリングが可能なようです。
pic090704_05.jpg

マウスホイールで拡大縮小操作ができますが、細部を拡大表示してもほとんどスピードは落ちません。
このSVG描画エンジンをvectexで使用できるとしたら、テクスチャ作成の時間はAGGを使っているオリジナルと同じか、もっと早くなるかもしれません。

対応しているSVGの仕様はSVG 1.2 tinyというものでECMA scriptやDOMなどには対応していないということですが、vectexで使用する場合はあまり関係なさそうです。

librsvgの時と同じようにサンプルプログラムを作ってみました。
qt_test.cpp
#include <QtGui>
#include <QtSvg>
#include <stdlib.h>

int main(int argc, char **argv)
{   
    QApplication app(argc, argv, QApplication::Tty);
    
    QString svgfname = "/home/mato/Desktop/sample/tiger.svg";
    QSvgRenderer svg_render;
    svg_render.load(svgfname);
   
    QSize size = svg_render.defaultSize();
    unsigned char          *buf = (unsigned char *)malloc(size.width() * size.height() * 4);

     QImage image(buf, size.width(), size.height(), QImage::Format_ARGB32);
    
    QPainter painter(&image);
    painter.setRenderHint(QPainter::Antialiasing);
   
    QColor col;
    col.setRgbF(1.0, 0.5, 0.0, 1.0);
    painter.fillRect(image.rect(), col);
       
    QTransform transform;
    transform.reset();
    transform.translate(-128, -128);
    transform.scale(2.0, 2.0);
    painter.setTransform(transform);
   
    svg_render.render(&painter);

    QString fname = "Hello.png";
     image.save(fname, "PNG", -1);
    
    free(buf);
 
    return 0;
}

qt_test.pro
######################################################################
# Automatically generated by qmake (2.01a) ? 6? 26 23:46:27 2009
######################################################################

TEMPLATE = app
TARGET =
DEPENDPATH += .
INCLUDEPATH += .

# Input
SOURCES += qt_test.cpp

QT += svg

Qtを使ったプログラムはコンパイルの際に、普通とはちょっと違ったQMakeというコマンドを使うようです。
qt_test.proというのは、そのQMakeで使用するコンパイルの設定ファイルです。
設定する内容はSconsのSConscriptと似たような感じのものですが、QMakeというコマンドはこのファイルを使って直接コンパイルするのではなくて、GCCなどの別のコンパイラで使えるMakefileを作成することになります。
pic090704_06.jpg

vectexで使用することを想定して、mallocで確保したメモリの使用、変換行列でのスケールなどを行っています。
QMakeという独自のツールを使っているので、Blenderのビルドシステムとの相性がちょっと気になりますが、普通のMakefileが作成されているのでインクルードファイルやライブラリ名などを調べるのは問題なくできそうです。

本来は、QtはGUIを使ったプログラミングを行うためのものですが、QApplicationのコンストラクタで指定することでコンソールアプリケーションとして使用できるようです。

ただし、Qtを使ったプログラムでは、main()関数の中でQApplicationのインスタンスを必ず作成しなければならない、ということが気になります。
はたして、これをBlenderに組み込むことができるのでしょうか...。

かなり無理があるような気もしますが、main()関数のあるファイルcreator.cの拡張子を「.c」から「.cpp」に変えることで無理やり組み込むことができました。

次回の記事で、詳しい内容について書きたいと思います。
posted by mato at 01:10| Comment(0) | Blender Vectex | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。