NanoVG.dの紹介

数日前にDUBにしれっと登録した「NanoVG.d(http://code.dlang.org/packages/nanovg-d )」の紹介をします。

## NanoVGについて
検索すればたぶんだいたいの情報は出てくるかと思いますが簡単に紹介しておきます。
NanoVG([memononen/nanovg](https://github.com/memononen/nanovg))とはMikko Mononen氏制作の2Dベクターグラフィックスライブラリです。HTML5のcanvas APIをもとに設計されており、コンパクトながら綺麗なベクターグラフィックスを簡単に利用することができます。

## NanoVG.dについて
上記NanoVGですが、C言語インターフェイスで提供されているので基本的にはextern(C)で持ってくるだけで使用することができます。ただ、C言語インターフェイスなので基本的に開放処理は自前で明示的に書くことになり、若干手間で多分殆どの人は忘れるでしょうからD言語用にラップしたインターフェイスを用意しました。
また、NanoVG単体では描画情報およびソフトウェアテッセレーション機能のみを実装しているため、(この手のライブラリではもはや当たり前ですが)別途NanoVGの仕様に合わせたレンダラを実装するか持ってくる必要があります。NanoVG本家にはOpenGL2,3,ES2,ES3に対応したレンダラが標準で実装されており、またDirectXで再実装されたレンダラもあるのでC/C++で使う上では困りませんが、Dでは使えないので(外部ライブラリとしてではなくソースコードとして組み込むことになるため)OpenGL3バージョンのみ移植しました。
ライセンス形態はzlib/libpngライセンスとなります。動作確認は、現在のところGNU/Linux(Arch Linux+GNOME Shell)でのみ確認できております(たぶんWindowsでも動くかと思いますが、確認は取れてないので自己責任でおねがいします)。

## サンプル
README.mdの丸写しですが

```d
import derelict.glfw3.glfw3;
import derelict.opengl3.gl;
import std.string;

// import nanovg-d package
import nanovg;

void main()
{
    // Load/InitLibrary
    DerelictGL3.load();
    DerelictGLFW3.load();
    if(glfwInit() != GL_TRUE) throw new Exception("GLFW initialization failed.");
    scope(exit) glfwTerminate();

    // For Intel Graphics(Forced to use OpenGL 3.3 Core Profile)
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    // CreateWindow
    auto pWindow = glfwCreateWindow(640, 480, "NanoVG.d Example", null, null);
    if(pWindow is null) throw new Exception("GLFW Window creation failed.");
    pWindow.glfwMakeContextCurrent();
    // LazyLoading GL3
    DerelictGL3.reload();

    // CenteringWindow
    auto vm = glfwGetVideoMode(glfwGetPrimaryMonitor());
    pWindow.glfwSetWindowPos((vm.width - 640) / 2, (vm.height - 480) / 2);

    // CreateNanoVGContext/Font
    // (Download and place NotoSans font)
    auto context = new NanoVG.ContextGL3();
    auto fontid = context.createFont("font", "./NotoSans-Regular.ttf");

    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    while(!glfwWindowShouldClose(pWindow))
    {
        int w, h;
        pWindow.glfwGetFramebufferSize(&w, &h);
        glViewport(0, 0, w, h);

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        with(context)
        {
            beginFrame(w, h, cast(float)w / cast(float)h);
            scope(exit) endFrame();

            // Initialize
            fontFace = fontid;
            fontSize = 18.0f;
            fontBlur = 0;

            // Title Text
            textAlign = NanoVG.TextAlign.LEFT | NanoVG.TextAlign.TOP;
            fillColor = nvgRGBAf(0.0f, 0.0f, 0.0f, 1.0f);
            text(8, 8, "NanoVG.d Sample");

            // Rect1
            beginPath();
            rect(100, 100, 150, 30);
            fillColor = nvgRGBAf(1.0f, 0.75f, 0.0f, 0.5f);
            fill();

            // Rect2
            beginPath();
            rect(130, 120, 50, 50);
            fillColor = nvgRGBAf(0.0f, 0.5f, 1.0f, 0.75f);
            fill();

            // Rounded Rect
            beginPath();
            roundedRect(50, 50, 250, 250, 8.0f);
            fillColor = nvgRGBAf(0.0f, 0.0f, 0.0f, 0.25f);
            fill();

            // Centered Text
            textAlign = NanoVG.TextAlign.CENTER | NanoVG.TextAlign.TOP;
            fillColor = nvgRGBAf(1.0f, 1.0f, 1.0f, 1.0f);
            text(50 + 250 / 2, 50 + 4, "TextWindow Modoki");

            // Beizer Curve
            beginPath();
            moveTo(200, 200);
            bezierTo(200, 300, 200, 300, 300, 300);
            strokeColor = nvgRGBAf(0.0f, 0.0f, 0.0f, 1.0f);
            stroke();
        }

        pWindow.glfwSwapBuffers();
        glfwPollEvents();
    }
}
```

基本的にD言語向けインターフェイスは全部NanoVG名前空間(nanovg/package.dでRenamed Importしてる)に詰めてあります。C言語インターフェイスもグローバル空間にそのままおいてあるので、Cで書いたコードをそのままもってくることも可能です(文字列はtoStringzで変換しないといけませんが)。また、OpenGL3レンダラを使用する場合はバージョン指定にUseGL3Rendererを追加する必要があります。
C言語で書かれたコードをインターフェイスを用いて書きなおす場合、テキスト関連関数のendパラメータが省略可能だったり(nullになります)しますが基本的には関数名のnvgプレフィクスを除いたものがNanoVG.Contextのメソッド名になっているのでほぼそのまま書き換えることが可能です。一部関数はsetterになっていたりTransform系関数がNanoVG.Matrix3x2の中に移動していたりしますが。
(後々ドキュメントを整備する予定です。)

以上で(すごく短いですが)NanoVG.dの紹介はおしまいです。簡単に使える割に比較的ハイパフォーマンスなので、ゲームに使用したりUI作成に使用したりできて(D言語自体も割とハイパフォーマンスなので)いい感じです。