Jasmine Tea でサンタの絵文字🎅を表示したい

この記事は、 [Jasmine Tea アドベントカレンダー 2023](https://qiita.com/advent-calendar/2023/jasminetea) の第22日目です。

こんにちは！Jasmine Tea の開発に参加している松尾です。

なんとなく、季節柄か Jasmine Tea でサンタ🎅を表示したいと思ったのでやってみました。

Jasmine Tea には、PIC パターンという仕組みがあります。これは 16x16 のイメージデータが予めいくつも登録してあり、PIC パターンの番号を指定するだけで登録済みのイメージを画面に表示することができる仕組みです。これより高次の機能であるスプライトやアニメーションの元になっています。

また、Jasmine Tea のユーザーは独自の PIC パターンを登録することもできます。これには `data` 命令を使うようです。下記のような感じです。

[Jasmine Tea のドキュメント](https://jasminetea.app/docs/guides/pics)からの抜粋
```
p@=[]
for i=0 to 255
  read p@[i]
next
def pic 532,p@
put (100,100),532
end
data -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1, 3, 3,-1,-1,-1,-1,-1,-1, 3, 3,-1,-1,-1
data -1,-1, 3, 3, 3, 3,-1,-1,-1,-1, 3, 3, 3, 3,-1,-1
data -1,-1, 3, 3, 3, 3, 3,-1,-1, 3, 3, 3, 3, 3,-1,-1
data -1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,-1
data -1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,-1
data -1,-1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,-1,-1
data -1,-1,-1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,-1,-1,-1
data -1,-1,-1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,-1,-1,-1
data -1,-1,-1,-1, 3, 3, 3, 3, 3, 3, 3, 3,-1,-1,-1,-1
data -1,-1,-1,-1,-1, 3, 3, 3, 3, 3, 3,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1, 3, 3, 3, 3,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1,-1, 3, 3,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
data -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
```

なるほど。data から 256 個のピクセルを配列に読んで `def pic` 命令で登録するわけですね。Jasmine Tea で使用できるカラーパレットはあまり多くないので、色のマッピングをしてあげないといけないようですね。

js でチャチャッとスクリプトを書いてマッピングできるようにしてみましょう。

```typescript
interface color {
    r: number,
    g: number,
    b: number,
}

const colors: { [key: number]: color } = {
    0: { r: 0, g: 0, b: 0 },
    1: { r: 0, g: 0, b: 255 },
    2: { r: 255, g: 0, b: 0 },
    3: { r: 255, g: 0, b: 255 },
    4: { r: 0, g: 255, b: 0 },
    5: { r: 0, g: 255, b: 255 },
    6: { r: 255, g: 255, b: 0 },
    7: { r: 255, g: 255, b: 255 },
    8: { r: 119, g: 119, b: 119 },
    9: { r: 0, g: 0, b: 170 },
    10: { r: 170, g: 0, b: 0 },
    11: { r: 170, g: 0, b: 170 },
    12: { r: 0, g: 170, b: 0 },
    13: { r: 0, g: 170, b: 170 },
    14: { r: 170, g: 170, b: 0 },
    15: { r: 170, g: 170, b: 170 },
    16: { r: 77, g: 129, b: 166 },
    17: { r: 78, g: 60, b: 60 },
    18: { r: 86, g: 98, b: 208 },
    19: { r: 87, g: 164, b: 254 },
    20: { r: 97, g: 166, b: 75 },
    21: { r: 124, g: 210, b: 207 },
    22: { r: 138, g: 218, b: 255 },
    23: { r: 139, g: 131, b: 129 },
    24: { r: 142, g: 76, b: 80 },
    25: { r: 152, g: 128, b: 248 },
    26: { r: 161, g: 112, b: 142 },
    27: { r: 172, g: 148, b: 124 },
    28: { r: 198, g: 192, b: 192 },
    29: { r: 205, g: 217, b: 59 },
    30: { r: 210, g: 191, b: 161 },
    31: { r: 238, g: 146, b: 43 },
    32: { r: 240, g: 112, b: 182 },
    33: { r: 245, g: 84, b: 64 },
    34: { r: 248, g: 157, b: 156 },
    35: { r: 249, g: 232, b: 216 },
    36: { r: 250, g: 205, b: 80 },
    37: { r: 251, g: 232, b: 42 },
};

function getClosestColorNumber(c: color): number {
    let smallestDiff = 255 * 3;
    let ret = 0;
    for (const n in colors) {
        const target = colors[n];
        const diff =
            Math.abs(c.r - target.r)
            + Math.abs(c.g - target.g)
            + Math.abs(c.b - target.b);
        if (diff < smallestDiff) {
            smallestDiff = diff;
            ret = parseInt(n, 10);
        }
    }
    return ret;
}
```

あとは画像データを Jasmine Tea の `data` 命令に変換してあげれば良さそうです。今回は便利そうな Jimp というライブラリを使いました。

```typescript
(async () => {
    const j = await Jimp.read(args[2]);
    const result: number[][] = [];

    for (let y = 0; y < 32; y++) {
        result.push([]);
        for (let x = 0; x < 32; x++) {
            const c = j.getPixelColor(x, y);
            const rgb = Jimp.intToRGBA(c);
            const colorNumber = getClosestColorNumber(rgb);
            result[y].push(colorNumber);
        }
    }

    // Top left                                                                                                                                                    
    for (let y = 0; y < 16; y++) {
        console.log(`data ${result[y].slice(0, 16).join(',')}`);
    }
    // Top right                                                                                                                                                   
    for (let y = 0; y < 16; y++) {
        console.log(`data ${result[y].slice(16, 32).join(',')}`);
    }
    // Bottom left                                                                                                                                                 
    for (let y = 16; y < 32; y++) {
        console.log(`data ${result[y].slice(0, 16).join(',')}`);
    }
    // Bottom right                                                                                                                                                
    for (let y = 16; y < 32; y++) {
        console.log(`data ${result[y].slice(16, 32).join(',')}`);
    }

})();

```

アルファチャンネルの処理をしてないので、透明なところの表現ができてないですが、まあいいでしょう。こんな感じで[Jasmine Tea でサンタクロースを表示](https://jasminetea.app/studio/projects/qLZzLpr9M1aKmqKJfWTU)することができました〜

それでは、メリークリスマス🎅