MBrush (PrinCube) の研究② - node.js に移植しようとして先を越された
mb-node
とりあえず、MBrush のアプリを介さず CUI から画像を印刷できるようにした。
のだが、依存するレポジトリにも同様の機能が入ってしまったので、現状では存在意義が微妙……(泣
使い方
git clone --depth=1 --recursive https://github.com/hunamizawa/mb-node cd mb-node ./build.sh npm ci --production
MBrush を接続してから
node app.js input.png
これでWi-Fi 経由でプリンターに画像が転送される。USB 接続の場合は -c usb
を足してくだち。
複数枚の画像を印刷したい場合は
node app.js input1.png input2.png
とすれば、MBrush の元々のアプリで印刷領域を複数設定したのと同じ挙動になる。
前回の続き
WebAssembly (WASM) の存在は、多少は知っていた。C++ とか何かの言語で書いたソースを、Java でいうバイトコード、.NET でいう IL のような中間コードに変換して、Web ブラウザ上で実行できるようにする。JS よりも効率よく動作し、ランタイムをインストールする手間もない、お手軽クロスプラットフォーム、という話は聞いたことがあったが、実際に WebAssembly で実装されたアプリを見るのは初めてだった。
Chrome の開発者ツールで .wasm ファイルを覗くと、バイナリから勝手に human-readable なテキスト表現(S式)には変換してくれるが、アセンブリに近い言語なので読む気にはならない。他言語への移植は難しそう。
node.js に移植できそうな気がした
mbc.wasm
と同じディレクトリにある mbc.js
は、何かのツールで生成したコードのようである。調べたところ、Emscripten で C/C++ から WASM をコンパイルした際に生成される、JavaScript から WASM を呼ぶためのラッパーだと判明した。このラッパーを観察すると NODEFS
等、node.js に関係しそうな変数名が使われている。
で、前回話した、画像を .mbd 形式に変換する最中に、console に何かデバッグメッセージが流れてくる。その中にこんなのがあった。
mbc: (7) ["128", "168", "60", "-i0", "-o0", "-w0", "-s2"]
-i0
とか -o0
とか、なんかコマンドライン引数みたいだなあ、と思いつつ、この配列をどこで使ってるのか調べたところ、WASM の中にある callMain()
という関数に渡されている様子。いかにもコンソールアプリ臭がする名前だ。
また、実験の過程で次のような ImageMagick っぽいエラーメッセージを吐いており、この WASM には ImageMagick が静的リンクされていると推測している。
UnableToOpenBlob 'ori.png': No such file or directory @ error/blob.c/OpenBlob/3497.
InvalidArgument '-colorize' '100-geometry' at CLI arg 12 @ error/operation.c/CLISimpleOperatorImage/2020.
おそらく開発者は、PNG 画像を .mbd 形式のファイルに変換するために、まず
$ ./foobar 128 168 60 -i0 -o0 -w0 -s2
のように使うコンソールアプリを作り、クロスプラットフォーム対応のために WASM へ移植したのだろう。
mbc.wasm の仕様
- ファイル I/O は Emscripten の File API (仮想ファイルシステム)を介して行う*1
- 入力
- 元画像
ori.png
- 色補正用の
m.bmp
- 元画像
- 出力
- .mbd 形式のファイル
mb.dat
- .mbd 形式のファイル
コマンドの引数:
[String(brightness), String(saturation), String(density), '-i' + invert, '-o' + c_order, '-w' + c_width, '-s' + dpi_step]
各変数の意味は https://github.com/dukelec/mb/blob/master/doc/dev.md に書いてあった:
args: img_dat: input png image, in type: Uint8Array brightness: 0~200, default 100 saturation: 0~500, default 100 density: 1~100, default 100 cal.c,m,y: 1~255, default 255 invert: print direction: 0: left to right, 1: right to left c_order: 0: CMY, 1: CYM. default 0 c_width: cartridge chip width: 0: 4.4mm, 1: 4.9mm. default 0 dpi_step: dpi setting: 1: 1200x1200, 2: 1200x600, 4: 1200x300 st_cb: progress status callback function ret: .mbd file data, in type: Uint8Array
WASM の初期化が重い
node.js の起動 + WASM の初期化で結構時間がかかる。画像が複数枚あると意外とストレスを感じる。もっとサクッと処理して欲しいなぁ。
というわけで、サーバーを立てて HTTP 経由で画像を渡せるようにする実験をしている。これなら WASM の初期化はサーバープロセス起動時の一度だけで済むよね。拙作の mb-node は今後この向きで育てていきたい。
*1:node.js 用に NODEFS なるものが用意されていたけど、使い方がよく分からなかった