CairoでPNGを読み込み・表示

このページには行き当たりばったりな表現が含まれます。
まとまった記事を期待する方は閲覧にご注意ください。

PNGの読み込み

さて、線を引くだけではつまらないので、PNGも表示してみましょう。
SVGはどうやら簡単ではなさそうなので後回しにします。

表示したいPNG画像
表示したいPNG画像

今回はこの画像を読み込んでみます。
Cairoのマニュアルを読みながらがんばってみましょう。

cairo_surface_t*    cairo_image_surface_create_from_png (const char *filename);
Creates a new image surface and initializes the contents to the given PNG file.
(PNG Supportから引用)

英語ですが、ほとんど関数名と定義が内容を物語っているので読むのには苦労しません。
これを使ってプログラム開始時にPNGイメージを読み込むことにしましょう。

void Initialize()
{
	// PNGの読み込み
	pngimage = cairo_image_surface_create_from_png("../image/image.png");
}
(CairoTest3.cpp)

cairo_image_surface_create_from_pngに渡すファイル名は、日本語でも問題なく読み込めるようです。
ただ、UNICODE文字列だと受け付けないと思われます。
このInitialize関数は、ウィンドウ生成時、すなわちWM_CREATEメッセージが来たときに呼び出すようにしています。
文字通りプログラム開始直後にすると、なぜか最初の1回だけうまく表示されなかったのです。

PNGサーフェスの破棄

読み込んだPNGファイルはcairo_surface_t*として受け取ったので、cairo_surface_t*として破棄してやる必要があります。
こちらはPNGだからといって特別な関数があるわけではなく、普通のcairo_surface_destroyを使います。

void Terminate()
{
	// PNGの破棄
	cairo_surface_destroy(pngimage);
}
(CairoTest3.cpp)

このTerminate関数は、Initialize関数と対応しているので、WM_DESTROYメッセージが来たときに呼び出します。

表示

cairo_set_source_surfaceで画像を用意してcairo_paintで実際に表示という流れですね。

void OnPaint(HDC hdc)
{
	// サーフェスと描画コンテキストを生成
	cairo_surface_t* surface = cairo_win32_surface_create(hdc);
	cairo_t* cr = cairo_create(surface);

	// PNGを表示
	cairo_set_source_surface(cr, pngimage, 20.0, 10.0);
	cairo_paint(cr);

	// サーフェスと描画コンテキストを破棄
	cairo_destroy(cr);
	cairo_surface_destroy(surface);
}
(CairoTest3.cpp)

こんなふうに表示される。
こんなふうに表示される。

cairo_set_source_surfaceの3~4つめの引数は表示位置を示します。
小数にしたらそれはそれなりにアンチエイリアスがかかります。

変換を加えて描画してみる

Transformationsのページにいろいろあるので試してみましょう。
これらの関数はcairo_set_source_surfaceを呼び出す前に使う必要があります。

平行移動

cairo_translateで平行移動ができます。

cairo_translate(cr, 80.0, 40.0);
cairo_set_source_surface(cr, pngimage, 20.0, 10.0);
cairo_paint(cr);
(CairoTest3.cpp)

指定した分だけ右下に表示された。
指定した分だけ右下に表示された。

拡大縮小

cairo_scaleで拡大縮小ができます。

cairo_scale(cr, 1.5, 0.5);
cairo_set_source_surface(cr, pngimage, 20.0, 10.0);
cairo_paint(cr);
(CairoTest3.cpp)

余白も一緒に拡大縮小された。
余白も一緒に拡大縮小された。

回転移動

cairo_rotateで原点を中心として時計回りに回転できます。

cairo_rotate(cr, 0.314);
cairo_set_source_surface(cr, pngimage, 20.0, 10.0);
cairo_paint(cr);
(CairoTest3.cpp)

18度ぐらい回転した。
18度ぐらい回転した。

複数の変換を指定する

複数の変換をかけると、指定したのとは逆の順番で変形するようです。
内部的に行列計算を行っているようなので、そのへんが原因だと思われます。

cairo_translate(cr, 80.0, 40.0);
cairo_scale(cr, 1.5, 0.5);
cairo_rotate(cr, 0.314);
cairo_set_source_surface(cr, pngimage, 20.0, 10.0);
cairo_paint(cr);
(CairoTest3.cpp)

18度ぐらい回転して余白も一緒に拡大縮小されて指定した分だけ右下に表示された。
18度ぐらい回転して余白も一緒に拡大縮小されて指定した分だけ右下に表示された。

ほかにも、変換行列を直接指定してアフィン変換できるcairo_transformなんてのもあるようですが、行列を作るのが面倒なので今回は扱いません。

まとめ

  1. PNGの読み込みはcairo_image_surface_create_from_pngで行う。
  2. cairo_set_source_surfaceで画像を用意してcairo_paintで実際に表示。
  3. 今回のCPPファイル