ImageMagick埋め込み画像

# はじめに

ImageMagick は実行ファイル(正確にはmagickライブラリのファイル)の中に画像データを幾つか埋め込んでいます。

```shell:
% convert logo: logo.gif
```

といったコマンドを見る事があると思いますが、この logo: の事です。

|logo:|
|---|
|![logo.gif](https://qiita-image-store.s3.amazonaws.com/0/1798/9517a226-3728-c4f9-898c-36df71fd15e5.gif) |

画像ファイルを用意しなくても ImageMagick のコマンドをとりあえず試せる便利機能です。

ちなみに大文字小文字の区別はありません。内部的には大文字で処理されます。

```
% convert LOGO: logo.gif
% convert LogO: logo.gif
```

# 埋まっている画像

-list coder で MAGICK に対応するエントリを見ると何の画像が埋め込まれているか分かります。

```shell:
% convert -list coder | grep MAGICK
LOGO        MAGICK
GRANITE     MAGICK
ROSE        MAGICK
NETSCAPE    MAGICK
WIZARD      MAGICK
H           MAGICK
```

## 画像一覧

| logo: | wizard: |
|---|---|
|![logo.gif](https://qiita-image-store.s3.amazonaws.com/0/1798/9517a226-3728-c4f9-898c-36df71fd15e5.gif) | ![wizard.gif](https://qiita-image-store.s3.amazonaws.com/0/1798/d27aa3ac-3c76-0a1d-cd79-de49fddb6dcc.gif) |

| granite: | netscape: | rose: |
|---|---|---|
| ![granite.gif](https://qiita-image-store.s3.amazonaws.com/0/1798/7dc8b5cd-eebc-0638-dd35-882f6923ec6e.gif)| ![netscape.gif](https://qiita-image-store.s3.amazonaws.com/0/1798/274eb046-9978-16ce-4552-0e7533923486.gif) |![rose.png](https://qiita-image-store.s3.amazonaws.com/0/1798/d626ea2b-6f13-251d-b7de-77a24abae675.png)|



## H: と MAGICK:

この最後の H だけ特別で、埋め込み画像ではなく出力画像を C/C++配列のコード形式にします。

```shell
% convert -size 8x8 xc:white H:
/*
   (PNM).
*/
static const unsigned char
  MagickImage[] =
  {
    0x50, 0x34, 0x0A, 0x38, 0x20, 0x38, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00,
  };
```

デフォルトでは PNM ですが、-define magick:format=〜 指定すると GIF 等の任意のフォーマットで出力できます。

```shell
% convert -size 8x8 xc:white -define magick:format=GIF H:
/*
   (GIF).
*/
static const unsigned char
  MagickImage[] =
  {
    0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x08, 0x00, 0x08, 0x00, 0xF0, 0x00,
    0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00,
    0x00, 0x02, 0x07, 0x84, 0x8F, 0xA9, 0xCB, 0xED, 0x5D, 0x00, 0x00, 0x3B,

  };
```

-list coders の結果に含まれませんが、実は出力に於いて H: と同じ挙動をする MAGICK: 指定もあります。

```shell:
% convert -size 8x8 xc:black MAGICK:
/*
   (PNM).
*/
static const unsigned char
  MagickImage[] =
  {
    0x50, 0x34, 0x0A, 0x38, 0x20, 0x38, 0x0A, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF,
  };
```

出力のみの H: と違い MAGICK: は入力にも指定できます。(5系で MAGICK: が追加されたのですが、使い方は僕も忘れました)

# プログラム

## 画像のコード表現

coders/magick.c に画像データが C/C++配列で埋め込まれています。画像形式は GIF と PNM の2種類があります。

```c
/*
  Predefined ImageMagick images.
*/
static const unsigned char
  GraniteImage[] =
  {
    0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x80, 0x00, 0x80, 0x00, 0xf3, 0x00,
    0x00, 0xa0, 0xa0, 0xa0, 0xa0, 0x98, 0xa0, 0xa9, 0xb2, 0xa9, 0xa0, 0xa9,
＜略＞
  LogoImage[] =
  {
    0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x80, 0x02, 0xE0, 0x01, 0xF7, 0x00,
    0x00, 0x04, 0x07, 0x07, 0x06, 0x09, 0x0E, 0x0A, 0x0B, 0x0B, 0x14, 0x0B,
＜略＞
  NetscapeImage[] =
  {
    0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0xd8, 0x00, 0x90, 0x00, 0xf7, 0x00,
    0x00, 0xcc, 0xff, 0x00, 0xcc, 0xcc, 0x00, 0xcc, 0x99, 0x00, 0xcc, 0x66,
＜略＞
  RoseImage[] =
  {
    0x50, 0x36, 0x0a, 0x37, 0x30, 0x20, 0x34, 0x36, 0x0a, 0x32, 0x35, 0x35,
    0x0a, 0x30, 0x2f, 0x2d, 0x32, 0x30, 0x2e, 0x36, 0x32, 0x2f, 0x38, 0x33,
＜略＞
  WizardImage[] =
  {
    0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0xE0, 0x01, 0x80, 0x02, 0xF7, 0x00,
    0x00, 0x0C, 0x0A, 0x0B, 0x06, 0x05, 0x08, 0x13, 0x0C, 0x0C, 0x0C, 0x0B,
＜略＞
typedef struct _MagickImageInfo
{
  char
    name[MaxTextExtent],
    magick[MaxTextExtent];

  const void
    *blob;

  size_t
    extent;
} MagickImageInfo;

static const MagickImageInfo
  MagickImageList[] =
  {
    { "LOGO", "GIF", LogoImage, sizeof(LogoImage) },
    { "GRANITE", "GIF", GraniteImage, sizeof(GraniteImage) },
    { "NETSCAPE", "GIF", NetscapeImage, sizeof(NetscapeImage) },
    { "ROSE", "PNM", RoseImage, sizeof(RoseImage) },
    { "WIZARD", "GIF", WizardImage, sizeof(WizardImage) },
    { "", "", (const void *) NULL, 0 }
  };
``` 

# netscape： について

## パレット画像

先ほども画像を貼りましたが、netscape: が何故かパレット画像です。普通は Netscape ブラウザのロゴが出るのを期待しますよね。

|netscape:| こっちじゃないの？|
|---|---|
|![netscape.gif](https://qiita-image-store.s3.amazonaws.com/0/1798/274eb046-9978-16ce-4552-0e7533923486.gif)|![image.png](https://qiita-image-store.s3.amazonaws.com/0/1798/79e2b25d-6df4-a596-4333-1f048527d6bf.png)|

さて、これは画像に使われる色を数えると察しがつきます。

```
% convert netscape: netscape.gif
% identify -verbose -format "%k\n" netscape.gif
216
```

216 を因数分解すると 6x6x6 。つまり RGB色空間を R,G,B 各々5等分した格子点です。

|![rgbcube2.png](https://qiita-image-store.s3.amazonaws.com/0/1798/e09f0228-d197-38b5-d02f-32fecdcd9498.png)|
|---|
|http://app.awm.jp/misc/js/threejs/rgbcube2.html|
全てのRGB色を似たパレット色に吸収させると、このような色立体で表現できます。

|![rgbcube.png](https://qiita-image-store.s3.amazonaws.com/0/1798/a9175b0e-b9b0-48aa-ff96-9ebcb25f49c4.png)|
|---|
|http://app.awm.jp/misc/js/threejs/rgbcube.html |

255 / 5 => 51 = 0x33 の間隔で単調増加した数列を使います。

| 0 |  51 | 102  | 153 |  204 | 255  |
|---|---|---|---|---|---|
| 0x00 | 0x33 | 0x66 | 0x99 |0cc | 0xff |

この6種類の輝度の R,G,B を組み合わせた色パレットという事です。

## 昔の事情

Netscape ブラウザが世に現れた1994年。21世紀の現在と比べて VRAM が非常に高価で多くは積めなかった事もあり、モニタ上に同時発色できるのが256色限定という環境も珍しくない時代でした。
多くの RGB (bitdepth:8) データは 256x256x256 = 16777216 色(トゥルーカラーと呼ばれます)を保持できます。256 色では全然足りません。

そこで実際に表示する色を限定してパレット色とし、それ以外の色はパレット色を併置混色して色を作り出す、いわゆるディザ表示がよく利用されました。

なお、更にチープな環境用に 5x5x5 = 125色 ディザ無しのモードもあったようです。

|6x6x6 = 216色|5x5x5 = 125色|
|---|---|
|![rgbcube.png](https://qiita-image-store.s3.amazonaws.com/0/1798/a9175b0e-b9b0-48aa-ff96-9ebcb25f49c4.png)|![image.png](https://qiita-image-store.s3.amazonaws.com/0/1798/4b83abfe-39e2-8145-edf0-cf857e2dd5ba.png)|


## パレット指定で減色してみる (蛇足)

実際に、netscape パレットの 216色限定で表示してみましょう。

検証用画像をまず作ります。

```shell:
% convert -size 300x900 gradient:'#FFF-#0FF' -rotate 90 \
    -virtual-pixel Transparent +distort Polar 149 +repage \
    -rotate 90 -set colorspace HSB -colorspace RGB \
    -flatten gradient_hue_polar.png
```

|gradient_hue_polar.png|
|---|
|![gradient_hue_polar.png](https://qiita-image-store.s3.amazonaws.com/0/1798/1fcbe4f7-9ac5-250f-46e2-545c68e06a4b.png)|

### 6x6x6 = 216色 (ディザ有り)

ディザつき Netscape パレットです。

```
convert gradient_hue_polar.png -remap netscape.gif 666-dither.png
```

-remap オプションに指定した画像に含まれる色しか使わないようにする。といった減色処理が出来ます。

|666-dither.png|
|---|
|![666-dither.png](https://qiita-image-store.s3.amazonaws.com/0/1798/d64e515d-1074-29fb-1de5-8281ec411aff.png)|

これなら遠目に見れば何とか誤魔化せそうです。

### 6x6x6 = 216色 (ディザ無し)

更にディザをかけないとこうなります。+dither でディザを無効化します。オプションが基本的に -〜なので ImageMagick の +〜 はその逆を表します。

```shell:
% convert gradient_hue_polar.png +dither -remap netscape.gif 666+dither.png
```

|666+dither.png|
|---|
|![666+dither.png](https://qiita-image-store.s3.amazonaws.com/0/1798/34183b96-d878-57f2-041a-a44c88d66a2d.png)|

元の画像にない階調がくっきり出てしまっています。

### 5x5x5 = 125色 (ディザ無し)

ついでに 5x5x5 = 125 色のディザ無しだとこうなります。

```shell:
# 5x5x5パレット画像(palette125.png)作成
% convert -size 5x5 gradient:black-red black-red.png
% convert -size 5x5 gradient:black-green1 -rotate 270 black-green.png
% composite -compose plus black-red.png black-green.png black-red-green.png
% convert -size 125x5 tile:black-red-green.png black-red-green-tiled.png
% convert -size 5x5 gradient:black-blue -scale 100%x2500% -rotate 270 black-blue.png
% composite black-red-green-tiled.png -compose plus black-blue.png palette125.png
```

|palette125.png|
|---|
|![palette125.png](https://qiita-image-store.s3.amazonaws.com/0/1798/186713f2-0fde-d0a8-5cd1-945963b12b47.png)|
| ↓ 参考のため、その5倍表示 |
|palette125x5.png|
|![palette125x5.png](https://qiita-image-store.s3.amazonaws.com/0/1798/84922225-aa86-9b87-66a0-a5f8f08113c2.png)|

```shell:
% convert gradient_hue_polar.png +dither -remap palette125.png 555+dither.png
```

|555+dither.png|
|---|
|![555+dither.png](https://qiita-image-store.s3.amazonaws.com/0/1798/8a6ee8ae-787e-d011-32c8-25b1b43ca121.png)|

これはひどい。。(；'-')

## 216色の理由

なぜ216色止まりにして、256 目いっぱい使わないのかというと、6x6x6 の次にキリの良い 7x7x7 だと 343色になって溢れてしまうのもありますが、Windows や Mac が予約しているシステムカラーもありますし、背景のテーマ用に(もしかしたらブラウザ以外のアプリケーションも？)少しは色を残しておくという配慮があったようです。

- 参考) とほほの色入門・色見本 #補足
   - http://www.tohoho-web.com/wwwcolor.htm#ColorHosoku

その実装を元に Netscape 社が提唱したパレット仕様が、今では<strong>Webセーフカラー</strong>として世に知られています。

## おまけ

netscape: 画像がどのように作られているのか。R,G,B 別に分解するとこんなでした。

|netscape:| red | green | blue |
|---|---|---|---|
| ![image.png](https://qiita-image-store.s3.amazonaws.com/0/1798/064d37b4-69fa-0e90-e1db-28a60f7c0ec0.png) | ![image.png](https://qiita-image-store.s3.amazonaws.com/0/1798/36c767f8-ea47-2fa0-c286-d95560065d5b.png) | ![image.png](https://qiita-image-store.s3.amazonaws.com/0/1798/59a0945b-36b7-6df1-1b49-b7a4a3007c32.png) | ![image.png](https://qiita-image-store.s3.amazonaws.com/0/1798/43442b75-61db-216c-0af4-d70a2cc17df4.png)|
(色コンポーネント分解ツール) http://app.awm.jp/image.js/colorcomponent.html

実は 6x6x6 と同じ要領で 5x5x5 のパレット画像を作って参考の為に貼ろうとしたのですが、この組み合わせは偶数でないとできないので、諦めたのでした。

# 昔の ImageMagick の埋め込み画像

昔は Granite, Logo, Netscape のみでした。バージョンが上がる毎に以下のように増えてます。

|バージョン| ソース |埋め込み画像|
|---|---|---|
|ImageMagick-3|magick/encode.c | Granite,Logo,Netscape |
|ImageMagick-4|magick/logo.c   | 上記に同じ |
|ImageMagick-5|coders/magick.c | Granite,Logo,Netscape,Rose |
|ImageMagick-6|coders/magick.c | Granite,Logo,Netscape,Rose,Wizard |
|ImageMagick-7|coders/magick.c | 上記に同じ |

ディレクトリ構成が少しずつ変わってきたのが懐かしいです。

# 最後に

あまりマニアックな記事を1日目にあてるとハードルが上がって後が辛いので、考えられる限り最もゆるふわなテーマにしたつもりですが、思ったより手間がかかっちゃいました。工数見積もりが下手すぎです。。次の記事どうしよう。。

# 参考URL

- https://www.imagemagick.org/script/formats.php#builtin-images
-