Mokkosuで画像を表示させる

# 背景

プログラム言語[Mokkosu](http://lambdataro.sakura.ne.jp/mokkosu/)の“Mokkosu”とは熊本で何らかの意味がある方言らしい。ところで、「[アイドルマスター シンデレラガールズ](http://imas-cinderella.com/)」というアニメーションに、熊本弁をたくさん話すキャラクターが登場したので、これはMokkosuでこのキャラクターの画像を表示させるしかないと決意した。

# 作戦1

[Mokkosu](http://lambdataro.sakura.ne.jp/mokkosu/)の公式サイトにあるグラフィックスライブラリの説明を見たところ、どうも画像を表示するというそのままずばりの関数は提供されていないようであった。だが、ある座標$(x, y)$にRGBの色を付ける関数`draw_pixel`が用意されていたので、次のようなことをすればいい。

1. 画像を解析して、座標と色の情報を取り出す（この時の座標を`x`,`y`、色を`r`, `g`, `b`とする）
2. `draw_pixel gr r g b x y`という文字列を生成する

画像を解析する部分をMokkosuで書こうかとも思ったが、面倒になったのでググって適当に調べたら出てきたPythonで、次のようなプログラムを書いた。

```py3
from PIL import Image

img    = Image.open('./kanzaki.png')
rgbimg = img.convert("RGB")
(w, h) = rgbimg.size

print("""include "Graphics.mok";\n""")

print("""do scene "main" {
    ~Draw(gr) -> do""")

for x in range(0, w):
    for y in range(0, h):
        (r, g, b) = rgbimg.getpixel((x, y))
        print("\t\tdraw_pixel gr %d %d %d %d %d;" % (r, g, b, x, y))

print("""\tend;
    
    _ -> ();
};

do set_size %d %d;

do show_window "main";
""" % (w, h))
```

これによって、次のようなMokkosuファイルが生成される。

```
include "Graphics.mok";

do scene "main" {
    ~Draw(gr) -> do
		draw_pixel gr 209 153 153 0 0;
		draw_pixel gr 209 153 153 0 1;
		draw_pixel gr 209 153 153 0 2;
		draw_pixel gr 209 153 153 0 3;
		draw_pixel gr 209 153 153 0 4;
		draw_pixel gr 209 153 153 0 5;
		draw_pixel gr 209 153 153 0 6;
		draw_pixel gr 209 153 153 0 7;

# ...（省略）...

		draw_pixel gr 209 153 153 316 336;
		draw_pixel gr 209 153 153 316 337;
		draw_pixel gr 209 153 153 316 338;
		draw_pixel gr 209 153 153 316 339;
	end;
    
    _ -> ();
};

do set_size 317 340;

do show_window "main";
```

$317 \times 340$の画像について、生成されたMokkosuファイルのサイズは **3.8MB** になってしまったが、これが噂のマルチステージ・プログラミングなのかもしれない。
さっそくコンパイルして実行することにした。

```console
$ mono MokkosuCompiler.exe ../../../../Sample/Kanzaki.mok
 =============================================
  __  __         _     _                      
 |  \/  |  ___  | | __| | __ ___   ___  _   _ 
 | |\/| | / _ \ | |/ /| |/ // _ \ / __|| | | |
 | |  | || (_) ||   < |   <| (_) |\__ \| |_| |
 |_|  |_| \___/ |_|\_\|_|\_\\___/ |___/ \__,_|

 =============== Version 1.1.5 ===============


致命的なエラー:致命的なエラー:
System.StackOverflowException: The requested operation caused a stack overflow.
  at (wrapper managed-to-native) object:__icall_wrapper_mono_gc_alloc_obj (intptr,intptr)
  at (wrapper alloc) object:AllocSmall (intptr)
  at Mokkosu.TypeInference.Typeinf.MapTypeVar (System.Collections.Generic.Dictionary`2 map, Mokkosu.AST.MType type) [0x00000] in <filename unknown>:0 
  at Mokkosu.TypeInference.Typeinf.MapTypeVar (System.Collections.Generic.Dictionary`2 map, Mokkosu.AST.MType type) [0x00000] in <filename unknown>:0 
  at Mokkosu.TypeInference.Typeinf.MapTypeVar (System.Collections.Generic.Dictionary`2 map, Mokkosu.AST.MType type) [0x00000] in <filename unknown>:0 
  at Mokkosu.TypeInference.Typeinf.MapTypeVar (System.Collections.Generic.Dictionary`2 map, Mokkosu.AST.MType type) [0x00000] in <filename unknown>:0 
  at Mokkosu.TypeInference.Typeinf.MapTypeVar (System.Collections.Generic.Dictionary`2 map, Mokkosu.AST.MType type) [0x00000] in <filename unknown>:0 
  at Mokkosu.TypeInference.Typeinf.MapTypeVar (System.Collections.Generic.Dictionary`2 map, Mokkosu.AST.MType type) [0x00000] in <filename unknown>:0 
```

コンパイルエラーで失敗してしまった。

# 作戦2

恐らくソースコードのファイルサイズが異常に巨大すぎたのが敗因と考え、次は画素のRGBを一旦リストのリストとして書き出すように改良を行った。

```py3
from PIL import Image

img    = Image.open('./kanzaki.png')
rgbimg = img.convert("RGB")
(w, h) = rgbimg.size

print("""include "Graphics.mok";\n""")

print("""fun nth xs n =
    match (xs,n) {
        (_,n) ? n < 0 -> error "nth";
        ([],_) -> error "nth";
        (x::_,0) -> x;
        (_::xs,n) -> nth xs (n-1);
    };""")

rgb = []
for x in range(0, w):
    row = []
    for y in range(0, h):
        row.append( rgbimg.getpixel((x, y)) )

    rgb.append(row)

print("let rgb =", rgb, ";");
print("""do scene "main" {
    ~Draw(gr) ->
        ignore (for y <- 0 .. %d in
            for x <- 0 .. %d in
                let (r, g, b) = nth (nth rgb y) x in 
                draw_pixel gr r g b x y);
        """ % (h, w))

print("""
    _ -> ();
};

do set_size %d %d;

do show_window "main";
""" % (w, h))
```

できあがったMokkosuファイルは次のようになる。

```
include "Graphics.mok";

fun nth xs n =
    match (xs,n) {
        (_,n) ? n < 0 -> error "nth";
        ([],_) -> error "nth";
        (x::_,0) -> x;
        (_::xs,n) -> nth xs (n-1);
    };
let rgb = [[(209, 153, 153), (209, 153, 153), (209, 153, 153), (209, 153, 153), (209, 153, 153) ...] ...] ;
do scene "main" {
    ~Draw(gr) ->
        ignore (for y <- 0 .. 340 in
            for x <- 0 .. 317 in
                let (r, g, b) = nth (nth rgb y) x in 
                draw_pixel gr r g b x y);
        

    _ -> ();
};

do set_size 317 340;

do show_window "main";
```

ファイルサイズは **1.7MB** と先ほどの半分程度まで小さくなった。早速コンパイルした。

```console
$ mono MokkosuCompiler.exe ../../../../Sample/Kanzaki.mok
 =============================================
  __  __         _     _
 |  \/  |  ___  | | __| | __ ___   ___  _   _
 | |\/| | / _ \ | |/ /| |/ // _ \ / __|| | | |
 | |  | || (_) ||   < |   <| (_) |\__ \| |_| |
 |_|  |_| \___/ |_|\_\|_|\_\\___/ |___/ \__,_|

 =============== Version 1.1.5 ===============


（コンパイラは長い眠りについたまま帰ってこず……）
```

しばらく待ったが、面倒になって待機するのをやめた。

# 作戦3

MokkosuはC#の機能を呼び出すことができるらしい。C#など1mmも書いたことがなかったが、サンプルプログラムの中に[それらしいもの](https://github.com/lambdataro/Mokkosu/blob/master/Sample/FormSample01.mok)を発見したので、雑に改造してとりあえず画像を表示させるところまではやってみた。
なおこの時点でプログラム（Mokkosu）を生成する必要はなくなったので、手書きしている。

```
import "System.Windows.Forms.dll";
import "System.Drawing.dll";

using System;
using System.Windows.Forms;
using System.Drawing;

let form   = new Form();
let image  = new Bitmap("./kanzaki.png");
let width  = image.get_Width();
let height = image.get_Height();

do form.set_ClientSize(new Size(width, height));
do form.set_BackgroundImage(image);

do form.ShowDialog() |> ignore;
```

コンパイル。

```console
$ mono MokkosuCompiler.exe ../../../../Sample/Kanzaki.mok
 =============================================
  __  __         _     _
 |  \/  |  ___  | | __| | __ ___   ___  _   _
 | |\/| | / _ \ | |/ /| |/ // _ \ / __|| | | |
 | |  | || (_) ||   < |   <| (_) |\__ \| |_| |
 |_|  |_| \___/ |_|\_\|_|\_\\___/ |___/ \__,_|

 =============== Version 1.1.5 ===============


form : {System.Windows.Forms.Form}
image : {System.Drawing.Bitmap}
width : Int
height : Int

コンパイルに成功しました。
```

万来の拍手！実行してみた。

```console
$ mono Kanzaki.exe
（色々たくさん）

Thread 1 (process 5586):
#0  0x00007fff8a1b7902 in __wait4 ()
#1  0x000000010b12f3e9 in mono_handle_native_sigsegv ()
#2  0x000000010b18024d in mono_arch_handle_altstack_exception ()
#3  0x000000010b09fc54 in mono_sigsegv_signal_handler ()
#4  <signal handler called>
#5  0x0000000000000000 in ?? ()
#6  0x000000010b57653f in ?? ()

=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
=================================================================

zsh: abort      mono Kanzaki.exe
```

MacのMonoでMokkosuのバイナリがセグメンテーションフォルトするのは既知の問題なので、VMにインストールしたWindowsで実行した。

![スクリーンショット 2015-03-08 20.53.28.png](https://qiita-image-store.s3.amazonaws.com/0/10815/a5054bb7-1ec1-c6b6-98dc-5ffc2a70bcdb.png)

動いた！

# まとめ

Mokkosuでも画像を表示することができる！よかった！
彼女なら、“Mokkosu”の意味を教えてくれることだろう。