WebAssembly 版 ImageMagick 〜 magick-wasm の紹介

# はじめに

サーバでなくブラウザ側で画像処理が動く WebAssembly 版 ImageMagick 〜 magick-wasm の使い方と 2023年初頭での状況報告です。

- https://github.com/dlemstra/magick-wasm

magick-wasm 本体にまだブラックボックス要素があるので、本体自体の解説はせず、今回は、実際に magick-wasm を使う事ができた。でも機能的に物足りなく感じた。それだけの解説エントリです。

:::note warn
4月20日に開発者のアナウンスにて、次リリース(恐らく 0.0.20)から wasm を js ファイルに含めないとの宣言がありました。
https://twitter.com/MagickNET/status/1648982963491618817
この記事で説明した手順では動かせなくなるかもしれません。少なくとも index.mjs を import するだけでは駄目になりそうです。
:::

## 書いたきっかけ

magick-wasm 使いたいけど分からないと呟いてたら作者からリプライを貰ったので、自分で解説書くしかないなと。。٩( 'ω' )و

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr"> There is a demo repository here: <a href="https://github.com/dlemstra/magick-wasm-docs"> github.com/dlemstra/magic... </a> that uses the library with @vuejs. And you can see the result on this website: <a href="https://dlemstra.github.io/magick-wasm-docs/"> dlemstra.github.io/magick-wasm-do... </a> </p> <a href="https://twitter.com/yoya/status/1600977794808238080">午前5:46 2022年10月16日</a> </blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# まとめ

<ul>
<li> 一言でいうとアルファ版相当。絶賛開発中。バージョン 0.0.18 だし
<li> 入出力できる画像形式は充実している一方、画像処理の対応は控えめ
<li> 回転や輝度/色フィルター、合成も出来る。文字やベクター描画系は無し
</ul>
<ul>
<li> マニュアルの代わりに TypeScript + Vue.js サンプルが用意されている
<li> API は見た感じ Magick++ (ImageMagick の C++ API)相当。 .NET 版と同じ？
<li> 試したら、直接 JavaScript からも使えた
</ul>
<ul>
<li> npm install @imagemagick/magick-wasm でインストール可能
<li> @imagemagick/magick-wasm パッケージのビルド方法は不明。(権限の問題？)
<li> 読み込む magick-wasm の MJS ファイルは今のところ 2MB 程度。思ったより小さい
</ul>

以下のサイトにデモがあって気軽に試せます。ソースコードもあります。

- https://dlemstra.github.io/magick-wasm-docs/
  - (ソースコード) https://github.com/dlemstra/magick-wasm-docs/ (マニュアルではなく、使い方のサンプル)


# magick-wasm 関連レポジトリ

主に、以下の4つです。

- https://github.com/ImageMagick/ImageMagick
   -  言わずと知れた ImageMagick 本体。C で実装されていて C++ 等の API を持つ。
- https://github.com/dlemstra/Magick.Native
   -  ImageMagick の wasm ビルドツール。ビルドするだけなので C/C++ 風 API 。
- https://github.com/dlemstra/magick-wasm
   -  C++ 風 API の wasm を TS/JS から使い易くするラッパーやユーティリティ群
- https://github.com/dlemstra/magick-wasm-docs
   - 使い方のサンプルデモ。今のところ整理されたマニュアルはなく、動くドキュメントを目指してるっぽい？

本体をいじりたい場合は別ですが、TS/JS ライブラリとして使うだけであれば magick-wasm-docs を見ると良いです。

## Magick.Native 

- https://github.com/dlemstra/Magick.Native 

ImageMagick を色んな環境でビルドする為のレポジトリで、主に .NET ライブラリの開発に使っているようで、ここの移植対象として wasm  が追加された形です。ちなみに Docker ゲスト環境でビルドします。いまどきですね。

## magick-wasm

こちらが Magick.Native の wasm コードを取り込む、TS/JS ラッパーです。

- https://github.com/dlemstra/magick-wasm

なお、ビルドの仕方は分かりません。(@dlemstra さんから権限をもらう必要ある？)

:::note info
npm install で @dlemstra/magick-native パッケージの D/L が 401 Unauthorized エラーで止まります。(2023/1/7現在)
ちなみに、@dlemstra/magick-native は npm サーバでなく github にホストしてるので、何も設定しないと E404 、設定すると E401 エラーになります。
:::

- Your npm @dlemstra/magick-native seems to be missing or incorrect #32
  - https://github.com/dlemstra/magick-wasm/issues/32

出来れば改造してみたいのですが、とりあえず npm install で得られるファイルを使いましょう。

```shell
% npm install @imagemagick/magick-wasm
% ls -lR node_modules/\@imagemagick/magick-wasm
total 400
-rw-r--r--  1 yoya  staff   11358  1  9 18:23 LICENSE
-rw-r--r--  1 yoya  staff  183236  1  9 18:23 NOTICE
-rw-r--r--  1 yoya  staff    1979  1  9 18:23 README.md
drwxr-xr-x  5 yoya  staff     160  1  9 18:23 dist
-rw-r--r--  1 yoya  staff    1929  1  9 18:23 package.json

node_modules/@imagemagick/magick-wasm/dist:
total 87056
-rw-r--r--  1 yoya  staff     47280  1  9 18:23 index.d.ts
-rw-r--r--  1 yoya  staff  22306698  1  9 18:23 index.mjs
-rw-r--r--  1 yoya  staff  22213834  1  9 18:23 index.umd.js
```

## magick-wasm-docs

URL がマニュアルっぽいですが、今のところデモサンプルが動作します。

- https://github.com/dlemstra/magick-wasm-docs

こちらは普通にビルドして試せます。README の通りです。

```shell
% npm install
% npm run serve
```

とすれば、ローカルでサーバが立ち上がって試せますし。

```shell
% npm install
% npm run build
```

こちらで build すれば dist に結果ファイルが生成されて、適当なサーバに upload すれば Web サイトとして動きます。

- https://dlemstra.github.io/magick-wasm-docs/

何もいじらずに npm run build した場合は、上記のデモサイトと同じページが作れます。実際に試した結果が以下のサイトです。

- https://app.awm.jp/magick-wasm/magick-wasm-docs/

:::note warn
magick-wasm-docs は https://&lt;host名&gt;/magick-wasm-docs/ 決め打ちの設定なので、上記のように /magick-wasm/magick-wasm-docs/ のパスにしたい場合は、vue.config.js の publicPath を変更します。
```javascript:vue.config.js
module.exports = {
-    publicPath: '/magick-wasm-docs/',
+    publicPath: '/magick-wasm/magick-wasm-docs/',
```
:::

# 利用する

##  magick-wasm-docs の改造

ビルドして試せるという事は、気軽にコードを改造できるという事です。

magick-wasm-docs を改造していくと理解が早いと思います。

今回は折角なので例のレトロ変換を目指しましたが、magick-wasm に機能が足りなくて諦めました。 -ordered-dither と -map 相当の機能が不足してるんですよね。以下のが結果です。

- https://app.awm.jp/magick-wasm/magick-wasm-docs-retro/

VueJS の作法に従って、以下のように改造して、デモページを増やします。

- src/routes.ts で　retro の URL パスを追加
- views/demos/Retro/*.vue に retro ページ本体と処理。

```typescript:src/views/demos/Retro/Retro.vue
import { ImageMagick } from '@imagemagick/magick-wasm/image-magick'

const canvas = document.getElementById('canvasId');

ImageMagick.readFromCanvas(canvas, (image) => {
    image.resize(320, 240);
    image.sigmoidalContrast(0, 8, 0.5);  // 0: _sharpen flag
    image.modulate(new Percentage(100), new Percentage(200), 
                   new Percentage(100));
    // -ordered-dither 4x4,8,8,8 
    // +dither -map palette.png
    image.filterType = FilterType.Point;
    image.resize(new MagickGeometry('200%'));
    image.writeToCanvas(canvas);
})
```

差分はこちらにまとめました。

- https://github.com/yoya/magick-wasm-docs/compare/main...add/retro

## JavaScript での利用

ちなみに、JavaScript から直接使えます。npm install で生成される node_modules/@imagemagick/magick-wasm/dist/index.mjs ファイルを読み込むだけです。

:::note info
mjs 拡張子を import する際にブラウザが content-type チェックを行う為、HTTP サーバに mjs の設定を入れる必要があります。それができない場合は、拡張子を .mjs から .js に変えても import できますが、(少なくともファイル管理的に)お行儀が悪そうです。
:::

例えば、回転させるだけならこれにて出来ます。

```shell:
% npm install @imagemagick/magick-wasm
% cp node_modules/\@imagemagick/magick-wasm/dist/index.mjs \
     lib/magick-wasm/0.0.17/index.mjs
```

```html:rotate.html
<html>
  <body>
    <canvas id="canvasId"> </canvas>
    <script type="module">
      import { initializeImageMagick,
               ImageMagick } from './lib/magick-wasm/0.0.17/index.mjs';
      const canvas = document.getElementById('canvasId');
      initializeImageMagick().then(() => {
          ImageMagick.read("logo:", (image) => image.writeToCanvas(canvas));
          ImageMagick.readFromCanvas(canvas, (image) => {
              const degrees = 90;
              image.rotate(degrees);
              image.writeToCanvas(canvas);
          })
      }).catch(err => {
          console.error(err);
      });
    </script>
  </body>
</html>
```

以下のところに実際に動作します。

- https://app.awm.jp/magick-wasm/js/rotate.html

| rotate.html の表示|
|---|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1798/0cb9e06c-2bbe-b823-3af8-a1e57278d800.png) |

# magick-wasm の開発状況

- https://github.com/dlemstra/magick-wasm/tree/main/src
  - 特にこっち > https://github.com/dlemstra/magick-wasm/blob/main/src/magick-image.ts

ImageMagick の基礎的な部分はだいぶ実装が進んでいるので、API を追加すれば色々動きそうに見えます。デモでも試せるように、リサイズやフィルタ周りはだいぶ対応しているようです。

あと、画像合成も動きます。 (2つ画像を読み込む良い方法が分からないので、readFromCanvas は二度入れ子に使ってます)

```JavaScript:composite.html
      initializeImageMagick().then(() => {
          ImageMagick.read("logo:", (image) => image.writeToCanvas(canvas1));
          ImageMagick.read("wizard:", (image) => image.writeToCanvas(canvas2));
          ImageMagick.readFromCanvas(canvas1, (image1) => {
              ImageMagick.readFromCanvas(canvas2, (image2) => {
                  image1.composite(image2, CompositeOperator.Darken);
                  image1.writeToCanvas(canvas3);
              })
          })
      }).catch(err => {
          console.error(err);
      });
```

- https://app.awm.jp/magick-wasm/js/composite.html

| composite.html の表示|
|---|
|![スクリーンショット 2023-01-16 3.31.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1798/7b4d5319-9301-c4f4-421f-e8b2795c443a.png)|

描画系 API は文字周りは用意されているのですが、試した感じ今のところ動く気配がないです。おそらく開発中かも？

# 備考

## WebAssembly 版 ImageMagick

実は WebAssembly 版 ImageMagick はずっと前から色んな人が公開しているのですが、今回紹介したパッケージ以外は、いずれもメンテナンスが止まってます。

- https://www.npmjs.com/package/wasm-imagemagick (ImageMagick公式から紹介されているサイト。多分、これが本命だったはず)
- https://www.npmjs.com/package/magick-wasm

ちなみに今回紹介したレポジトリの所有者 dlemstra さんは ImageMagick の .NET 版開発者で、ImageMagick のコア開発者としても、原作者 cristy さんと並んで活躍してる方です。