システム開発・Webサイト構築 プラスラス

2010/10/28 木曜日

【.NET】VB/C#でグラフィックライブラリcairoを使う

このエントリーを含むはてなブックマーク Yahoo!ブックマークに登録 Google ブックマーク del.icio.us

cairoは、クロスプラットフォームの2Dグラフィックライブラリです。ベクタグラフィックエディタのInkspaceにも使用されているベクタベースグラフィック描画エンジンのAPIを使用できます。

image

具体的にどのようなことができるのかは、cairoのサイトのサンプルなどをご覧ください。

image

http://cairographics.org/

http://cairographics.org/samples/

cairoのライセンスは、LGPL 2.1または、MPL 1.1の選択制です。


cairoの入手

cairo自体が他のライブラリに依存しているため、自分でファイルを集めるのが少々面倒です。

そこで、GTK+のサイトで公開されているWindows用All-in-one bundlesを手に入れて、そこから必要なDLLを取り出して使います。

http://www.gtk.org/download-windows.html

image

今回は、2.22のをダウンロードしました。

.NETのアプリからは、P/Invokeを使用して、WindowsネイティブコードのDLLを使用することになります。ダウンロードしたzipを展開して、binフォルダの中から、以下のファイルを取り出してください。(ただし、使用するバージョンによっては、ファイル名の数字の部分が違うかもしれません。)

  • libcairo-2.dll
  • freetype6.dll
  • libexpat-1.dll
  • libfontconfig-1.dll
  • libpng14-14.dll
  • zlib1.dll

これらのファイルを.NETアプリのexeと同一のフォルダに置いて使用します。Visual Studioで.NETアプリをデバッグするには、bin/Debugフォルダに自分でコピーします。(.NETのアセンブリファイルではないので、参照設定に追加するのではありません。)

もしくは、プロジェクトにファイルを追加して、exeと同じパスに自動でコピーされるようにしても良いです。

この他に、WindowsのCランタイム(VC++6用) msvcrt.dllが必要です。現在使われているWindows(XP、Vista、7等)には最初から入っていると思いますが、必要であれば別途入手してください。

Vcredist.exe による Visual C++ 6.0 アプリケーション用の最新ランタイム コンポーネントのインストール

libcairo-2.dllからリンクしているライブラリについて

一応、上記のGTK+のページにある cairoのWindows用のバイナリであるlibcairo-2.dll(Ver. 1.10.0)から静的リンクされているライブラリも調べたところ、以下の通りでした。LGPLやMPLとして使用して問題ないと思います。

ただし、ライセンスが変更されたり、リンクしているものが変わる可能性もあるので、あらかじめ調べてから使ってください。

FreeType

http://savannah.nongnu.org/projects/freetype/

GPLまたはFreeType License(BSD Licenseに類似したライセンス)

libpng

http://www.libpng.org/pub/png/libpng.html

独自ライセンス(zlibに類似したライセンス)

Expat XML Parser

http://sourceforge.net/projects/expat/

MIT License

Fontconfig

http://www.fontconfig.org/wiki/

Permissive free software licence(パブリックドメイン)

zlib

http://zlib.net/

zlibライセンス

.NETアプリでcairoを使用してみる

以下のサンプルは、http://cairographics.org/samples/に掲載されているサンプルを.NET用に一部書き換えたものです。

実行するには、このページの上の方に書いてあるように、.NETアプリのexeと同じフォルダにDLL(libcairo-2.dll、freetype6.dll、libexpat-1.dll、libfontconfig-1.dll、libpng14-14.dll、zlib1.dll)を配置する必要があります。

cairoを使用したプログラムの手順としては、目的にあったsurface(Windowsのデバイスコンテキストへの描画ならcairo_win32_surface_create()、特定のイメージフォーマットへの描画ならcairo_image_surface_create()などで)を作成し、cairo_create()でコンテキストを取得します。

次に、取得したコンテキストを引数に指定し、任意の処理(座標の移動、線、面の描画等)を行ないます。

最後にリソースを破棄して終了します。

この辺りの概念は、チュートリアル http://cairographics.org/tutorial/ で説明されています。

libcairo-2.dllの中の関数を呼ぶためには、DllImportで定義する必要があります。展開したzipの中のinclude\cairoにあるヘッダファイルを参考にすると楽です。

ほとんどの関数は、描画用のハンドルとして使うポインタと数値を引数にとるだけなので、IntPtrやdoubleに機械的に置き換えできると思います。

文字列については、UTF-8を受け取るようにできているので、byteの配列にします。

下のサンプルでは、フォームを一つ作成し、フォームのPaintイベントの中で描画処理を行っています。

サンプルarc

'VB.NETのサンプル
Public Class Form1
    Inherits Form

    '(デザイナによる自動生成コードは省略)

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Function cairo_win32_surface_create(ByVal hdc As System.IntPtr) _
    As System.IntPtr
    End Function

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Function cairo_create(ByVal surface As System.IntPtr) _
    As System.IntPtr
    End Function

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_set_line_width(ByVal cr As System.IntPtr, _
    ByVal value As Double)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_arc(ByVal cr As System.IntPtr, ByVal xc As Double, _
                                 ByVal yc As Double, ByVal radius As Double, ByVal angle1 As Double, _
                                 ByVal angle2 As Double)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_stroke(ByVal cr As System.IntPtr)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_set_source_rgba _
    (ByVal cr As System.IntPtr, ByVal red As Double, _
                                             ByVal green As Double, ByVal blue As Double, _
                                             ByVal alpha As Double)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_line_to _
    (ByVal cr As System.IntPtr, ByVal xc As Double, ByVal xy As Double)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_fill(ByVal cr As System.IntPtr)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_destroy(ByVal cr As System.IntPtr)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_surface_destroy(ByVal surface As System.IntPtr)
    End Sub

    Private Sub Form1_Paint(ByVal sender As Object, _
        ByVal e As PaintEventArgs) Handles MyBase.Paint

        Dim hdc As System.IntPtr = e.Graphics.GetHdc()
        Dim surface As IntPtr = cairo_win32_surface_create(hdc)
        Dim cr As IntPtr = cairo_create(surface)

        Dim xc As Double = 128.0
        Dim yc As Double = 128.0
        Dim radius As Double = 100.0
        Dim angle1 As Double = 45.0 * (System.Math.PI / 180.0)
        ' angles are specified 
        Dim angle2 As Double = 180.0 * (System.Math.PI / 180.0)
        ' in radians           

        cairo_set_line_width(cr, 10.0)
        cairo_arc(cr, xc, yc, radius, angle1, angle2)
        cairo_stroke(cr)

        ' draw helping lines 

        cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.6)
        cairo_set_line_width(cr, 6.0)

        cairo_arc(cr, xc, yc, 10.0, 0, 2 * System.Math.PI)
        cairo_fill(cr)

        cairo_arc(cr, xc, yc, radius, angle1, angle1)
        cairo_line_to(cr, xc, yc)
        cairo_arc(cr, xc, yc, radius, angle2, angle2)
        cairo_line_to(cr, xc, yc)
        cairo_stroke(cr)

        'リソース破棄
        cairo_destroy(cr)
        cairo_surface_destroy(surface)
        e.Graphics.ReleaseHdc(hdc)

    End Sub

End Class
C#のサンプル
public class Form1 : Form
{
        //(デザイナによる自動生成コードは省略)

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static System.IntPtr cairo_win32_surface_create(System.IntPtr hdc);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static System.IntPtr cairo_create(System.IntPtr surface);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_set_line_width(System.IntPtr cr, double value);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_arc(System.IntPtr cr, double xc,
            double yc, double radius, double angle1, double angle2);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_stroke(System.IntPtr cr);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_set_source_rgba(System.IntPtr cr,
        double red, double green, double blue, double alpha);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_line_to(System.IntPtr cr, double xc, double xy);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_fill(System.IntPtr cr);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_destroy(System.IntPtr cr);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_surface_destroy(System.IntPtr surface);

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            System.IntPtr hdc = e.Graphics.GetHdc();
            IntPtr surface = cairo_win32_surface_create(hdc);
            IntPtr cr = cairo_create(surface);

            double xc = 128.0;
            double yc = 128.0;
            double radius = 100.0;
            double angle1 = 45.0 * (System.Math.PI / 180.0);  /* angles are specified */
            double angle2 = 180.0 * (System.Math.PI / 180.0);  /* in radians           */

            cairo_set_line_width(cr, 10.0);
            cairo_arc(cr, xc, yc, radius, angle1, angle2);
            cairo_stroke(cr);

            /* draw helping lines */
            cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.6);
            cairo_set_line_width(cr, 6.0);

            cairo_arc(cr, xc, yc, 10.0, 0, 2 * System.Math.PI);
            cairo_fill(cr);

            cairo_arc(cr, xc, yc, radius, angle1, angle1);
            cairo_line_to(cr, xc, yc);
            cairo_arc(cr, xc, yc, radius, angle2, angle2);
            cairo_line_to(cr, xc, yc);
            cairo_stroke(cr);

            //リソース破棄
            cairo_destroy(cr);
            cairo_surface_destroy(surface);
            e.Graphics.ReleaseHdc(hdc);
        }
 }

arcの実行結果

image

サンプルtext

'VB.NETのサンプル
Public Class Form1
    Inherits Form

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Function cairo_win32_surface_create(ByVal hdc As System.IntPtr) _
        As System.IntPtr
    End Function

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Function cairo_create(ByVal surface As System.IntPtr) As System.IntPtr
    End Function

    Private Enum cairo_font_slant_t
        CAIRO_FONT_SLANT_NORMAL
        CAIRO_FONT_SLANT_ITALIC
        CAIRO_FONT_SLANT_OBLIQUE
    End Enum

    Private Enum cairo_font_weight_t
        CAIRO_FONT_WEIGHT_NORMAL
        CAIRO_FONT_WEIGHT_BOLD
    End Enum

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_select_font_face(ByVal cr _
        As System.IntPtr, _
                                              ByVal family As Byte(), _
                                              ByVal slant As cairo_font_slant_t, _
                                              ByVal weight As cairo_font_weight_t)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_set_font_size(ByVal cr As System.IntPtr, ByVal size As Double)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_move_to _
        (ByVal cr As System.IntPtr, ByVal x As Double, ByVal y As Double)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_show_text(ByVal cr As System.IntPtr, ByVal utf8 As Byte())
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_text_path(ByVal cr As System.IntPtr, ByVal utf8 As Byte())
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_set_source_rgb(ByVal cr As System.IntPtr, _
                                            ByVal red As Double, _
                                            ByVal green As Double, _
                                            ByVal blue As Double)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_fill_preserve(ByVal cr As System.IntPtr)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_set_line_width _
        (ByVal cr As System.IntPtr, ByVal value As Double)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_stroke(ByVal cr As System.IntPtr)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_set_source_rgba(ByVal cr As System.IntPtr, _
                                             ByVal red As Double, ByVal green As Double, _
                                             ByVal blue As Double, ByVal alpha As Double)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_arc(ByVal cr As System.IntPtr, ByVal xc As Double, _
                                 ByVal yc As Double, ByVal radius As Double, _
                                 ByVal angle1 As Double, ByVal angle2 As Double)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_fill(ByVal cr As System.IntPtr)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_close_path(ByVal cr As System.IntPtr)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_destroy(ByVal cr As System.IntPtr)
    End Sub

    <System.Runtime.InteropServices.DllImport("libcairo-2.dll")> _
    Private Shared Sub cairo_surface_destroy(ByVal surface As System.IntPtr)
    End Sub

    Private Sub Form1_Paint(ByVal sender As Object, _
                            ByVal e As PaintEventArgs) Handles MyBase.Paint

        Dim hdc As System.IntPtr = e.Graphics.GetHdc()
        Dim surface As IntPtr = cairo_win32_surface_create(hdc)
        Dim cr As IntPtr = cairo_create(surface)

        cairo_select_font_face(cr, StringToByteArray("Sans"), _
                               cairo_font_slant_t.CAIRO_FONT_SLANT_NORMAL, _
                               cairo_font_weight_t.CAIRO_FONT_WEIGHT_BOLD)

        cairo_set_font_size(cr, 90.0)

        cairo_move_to(cr, 10.0, 135.0)
        cairo_show_text(cr, StringToByteArray("Hello"))

        cairo_move_to(cr, 70.0, 165.0)
        cairo_text_path(cr, StringToByteArray("void"))
        cairo_set_source_rgb(cr, 0.5, 0.5, 1)

        cairo_fill_preserve(cr)
        cairo_set_source_rgb(cr, 0, 0, 0)
        cairo_set_line_width(cr, 2.56)
        cairo_stroke(cr)

        ' draw helping lines 

        cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.6)
        cairo_arc(cr, 10.0, 135.0, 5.12, 0, 2 * System.Math.PI)
        cairo_close_path(cr)
        cairo_arc(cr, 70.0, 165.0, 5.12, 0, 2 * System.Math.PI)
        cairo_fill(cr)

        'リソース破棄
        cairo_destroy(cr)
        cairo_surface_destroy(surface)
        e.Graphics.ReleaseHdc(hdc)
    End Sub

    'UTF-8のバイト配列に変換
    Private Function StringToByteArray(ByVal s As String) As Byte()

        Dim source As Byte() = _
            System.Text.Encoding.GetEncoding("UTF-8").GetBytes(s)

        Dim result As Byte() = New Byte(source.Length) {}

        Array.Copy(source, result, source.Length)

        result(result.Length - 1) = 0

        Return result
    End Function

End Class
//C#のサンプル

public  class Form1 : Form
{
       //(デザイナによる自動生成のコードは省略)

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static System.IntPtr cairo_win32_surface_create(System.IntPtr hdc);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static System.IntPtr cairo_create(System.IntPtr surface);

        enum cairo_font_slant_t
        {
            CAIRO_FONT_SLANT_NORMAL,
            CAIRO_FONT_SLANT_ITALIC,
            CAIRO_FONT_SLANT_OBLIQUE
        }

        enum cairo_font_weight_t
        {
            CAIRO_FONT_WEIGHT_NORMAL,
            CAIRO_FONT_WEIGHT_BOLD
        }

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_select_font_face(System.IntPtr cr,
                                                            byte[] family,
                                                            cairo_font_slant_t slant,
                                                            cairo_font_weight_t weight);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_set_font_size(System.IntPtr cr, double size);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_move_to(System.IntPtr cr, double x, double y);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_show_text (System.IntPtr cr, byte[] utf8);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_text_path(System.IntPtr cr, byte[] utf8);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_set_source_rgb(System.IntPtr cr,
                                                            double red,
                                                            double green,
                                                            double blue);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_fill_preserve(System.IntPtr cr);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_set_line_width(System.IntPtr cr,
                                                        double value);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_stroke(System.IntPtr cr);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_set_source_rgba(System.IntPtr cr,
                                                               double red,
                                                               double green,
                                                               double blue,
                                                               double alpha);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_arc(System.IntPtr cr,
                                                double xc,
                                                double yc,
                                                double radius,
                                                double angle1,
                                                double angle2);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_fill(System.IntPtr cr);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_close_path(System.IntPtr cr);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_destroy(System.IntPtr cr);

        [System.Runtime.InteropServices.DllImport("libcairo-2.dll")]
        private extern static void cairo_surface_destroy(System.IntPtr surface);

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            System.IntPtr hdc = e.Graphics.GetHdc();
            IntPtr surface = cairo_win32_surface_create(hdc);
            IntPtr cr = cairo_create(surface);

            cairo_select_font_face(cr,
                                   StringToByteArray("Sans"),
                                   cairo_font_slant_t.CAIRO_FONT_SLANT_NORMAL,
                                   cairo_font_weight_t.CAIRO_FONT_WEIGHT_BOLD);

            cairo_set_font_size(cr, 90.0);

            cairo_move_to(cr, 10.0, 135.0);
            cairo_show_text(cr, StringToByteArray("Hello"));

            cairo_move_to(cr, 70.0, 165.0);
            cairo_text_path(cr, StringToByteArray("void"));
            cairo_set_source_rgb(cr, 0.5, 0.5, 1);

            cairo_fill_preserve(cr);
            cairo_set_source_rgb(cr, 0, 0, 0);
            cairo_set_line_width(cr, 2.56);
            cairo_stroke(cr);

            /* draw helping lines */
            cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.6);
            cairo_arc(cr, 10.0, 135.0, 5.12, 0, 2 * System.Math.PI);
            cairo_close_path(cr);
            cairo_arc(cr, 70.0, 165.0, 5.12, 0, 2 * System.Math.PI);
            cairo_fill(cr);

            //リソース破棄
            cairo_destroy(cr);
            cairo_surface_destroy(surface);
            e.Graphics.ReleaseHdc(hdc);
        }

        //UTF-8のバイト配列に変換
        private byte[] StringToByteArray(string s)
        {
            byte[] source = System.Text.Encoding.GetEncoding("UTF-8").GetBytes(s);

            byte[] result = new byte[source.Length + 1];

            Array.Copy(source, result, source.Length);

            result[result.Length - 1] = 0;

            return result;
        }
    }

textの実行結果

image

Mono.Cairoについて

しかし、いちいち、DllImportを書くのは面倒です。.NET用のcairoラッパーがMono.Cairoが、Monoプロジェクトの中にあります。

Mono.Cairoは、gtk#をダウンロードすれば含まれているのですが、現状、日本語のテキストをうまく処理できません。と言っても、このページのサンプルにあるようなstringをUTF-8のバイト配列に変換する処理が入っていないというだけなので、簡単に直るものと思います。

詳細については、後日書こうと思っています。

Mono Project http://mono-project.com/Main_Page


関連図書


タグ: ,
Filed under: C#,Programming,VB.NET — Nakai @ 23:23:09

コメントはまだありません »

コメントはまだありません。

この投稿へのコメントの RSS フィード。 TrackBack URL

コメントする

HTML convert time: 0.938 sec. Powered by WordPress