NanoVG で HTML5 Canvas の力を C/C++ にも


# NanoVG って何？

[NanoVG](https://github.com/memononen/nanovg) は C 言語で書かれた（現在なお開発中の）2D 描画ライブラリである。以下特徴を書く。

 - HTML5 Canvas に近い API を持つ
 - 本体が約 4000 行程度とコンパクト
 - OpenGL を利用して描画するため非常に高速
  - OpenGL 2/3 と OpenGL ES 2/3 にそれぞれ最初から対応
  - レンダリング部分はモジュール化されて分離されてるため自作可能
 - zlib ライセンス

いかに強力かはビルドして出来上がる build/example_gl* を実行することでわかるだろう。

# NanoVG をビルドする

ビルドするには [premake4](http://industriousone.com/premake/) と [glfw3](http://glfw.org) がそれぞれ必要なので事前にインストールする。ここでは OSX でかつ macports をインストールしていること前提で話を進める。

```bash
sudo port install premake4 glfw
# /opt/local にインストールしている場合はシンボリックリンクを貼る必要があるかもしれない
# sudo ln -s /opt/local/include/GLFW /usr/local/include/GLFW
# sudo ln -s /opt/local/lib/libglfw.3.dylib /usr/local/lib/libglfw3.dylib
git clone https://github.com/memononen/nanovg.git
cd nanovg
premake4 gmake
cd build
# デスクトップの OpenGL 2/3 のみビルドする
make example_gl[23]*
```

# 簡単な処理フロー

```c
/* 大きさはここでは適当に指定しているが、実際は関数を使ってサイズとデバイスのピクセルレートを求めるだろう */
int width = 512;
int height = 512;
float pixelRatio = 1.0
;
/*

NanoVG 用のコンテキストを作成する。HTML5 Canvas では canvas タグに id="canvas" を付加した想定で以下のコードに相当する。

  var context = document.getElementById("canvas").getContext("2d");

呼び出す関数は利用したい OpenGL のバージョンによって異なるので、ここでは OpenGL2 の関数を使用する

 */
NVGcontext *context = nvgCreateGL2(width, height, NVG_ANTIALIAS);

while (rendering) {
    /* nvg* の関数を呼び出す前にかならず nvgBeginFrame を呼び出す必要がある */
    nvgBeginFrame(context, width, height, pixelRatio, NVG_PREMULTIPLIED_ALPHA);

    /* ここで nvg* の関数を呼び出して実際に処理を行う */

    /* 完了したら nvgEndFrame を呼び出して描画する */
    nvgEndFrame(context);
}

/* 終了時にコンテキストを破棄する */
nvgDeleteGL2(context);
```

# NanoVG における Q/A っぽいもの

## Q. 日本語も表示できる？ A. フォントがあれば表示できる

まず予めフォントデータを ``nvgCreateFont`` または ``nvgCreateFontMem`` 関数を使ってフォントデータを読みだしておき、``nvgFontFace`` で指定して ``nvgBeginPath`` を呼び出してから ``nvgText`` を使うと表示できる。


```c
nvgCreateFont(context, "japanese_font", "/path/to/japanese_font.ttf");
nvgFontFace(context, "japanese_font");
nvgBeginPath(context);
/* y は中心位置なことに注意。実際ここの部分は nvgTextBounds や nvgTextMetrics などを使って計算して求める必要があるだろう */
int x = 0;
int y = 10;
nvgText(context, x, y, "こんにちは世界！", NULL);
```

## Q. 画像を表示させたい場合はどうするの？ A. NVGpattern + nvgFillPattern + nvgFill

以下即席コード

```cpp
struct Image {
    Image(NVGcontext *c)
        : context2D(c),
          handle(0),
          width(0),
          height(0)
    {
    }
    ~Image() {
        nvgDeleteImage(context2D, handle);
        handle = 0;
        context2D = 0;
    }
    void load(const char *filename) {
        handle = nvgCreateImage(context2D, filename);
        nvgImageSize(context2D, handle, &width, &height);
        pattern = nvgImagePattern(context2D, 0, 0, width, height, 0, handle, 1);
    }
    void render() {
        nvgBeginPath(context2D);
        nvgRect(context2D, 0, 0, width, height);
        nvgFillPaint(context2D, pattern);
        nvgFill(context2D);
    }
    NVGcontext *context2D;
    NVGpaint pattern;
    int handle;
    int width;
    int height;
};
```

内部的には stb_truetype.h の API の wrapper なので、stb_truetype.h で出来ないことは NanoVG でも出来ない。

# NanoVG API と HTML5 Canvas API の対照表

基本的な処理の API だけピックアップする。

|NanoVG|Canvas API|
|---|---|
|nvgBeginPath(context)|Context2D#beginPath()|
|nvgMoveTo(context, x, y)|Context2D#moveTo(x, y)|
|nvgLineTo(context, x, y)|Context2D#lineTo(x, y)|
|nvgBezierTo(context, c1x, c1y, c2x, c2y, x, y)|Context2D#bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)|
|nvgArcTo(context, x1, y1, x2, y2, radius)|Context2D#arcTo(x1, y1, x2, y2, radius)|
|nvgClosePath(context)|Context2D#closePath()|
|nvgRect(context, x, y, w, h)|Context2D#rect(x, y, w, h)|
|nvgRoundedRect(context, x, y, w, h, r)|Context2D#roundedRect(x, y, w, h, xradius, yradius)|
|nvgEllipse(context, cx, cy, rx, ry)|Context2D#ellipse(x, y, w, h)|
|nvgFill(context)|Context2D#fill()|
|nvgStroke(context)|Context2D#stroke()|
|nvgStrokeColor(context, color)|Context2D#strokeColor|
|nvgFillColor(context, color)|Context2D#fillColor|
|nvgMiterLimit(context, limit)|Context2D#miterLimit|
|nvgStrokeWidth(context, width)|Context2D#strokeWidth|
|nvgLineCap(context, cap)|Context2D#lineCap|
|nvgLineJoin(context, join)|Context2D#lineJoin|

# 2D に関連する描画ライブラリまたは機能及び仕様の一覧

それぞれ API は異なるが、いずれも HTML5 Canvas に必要な機能をある程度持っている。

 - [Cairo](http://cairographics.org/) (LGPL/MPL)
 - [Skia](https://code.google.com/p/skia/) (3条項BSDL)
 - [Qt](http://qt-project.org/) (GPL/LGPL)
 - [Anti Grain Geometry](http://agg.sourceforge.net/antigrain.com/index.html) (N/A)
  - 2.4 までは 3条項BSDL だったが 2.5 から GPL
  - 2.4 と 2.5 は機能的に同じ
 - [OpenVG](https://www.khronos.org/openvg/) (N/A)
  - OpenVG は現在仕様の保守のみで新機能に対する策定は行われていない
 - [NV_path_rendering](https://developer.nvidia.com/nv-path-rendering) (N/A)
  - OpenGL の拡張であり、現状 NVIDIA の GPU でしか利用できない