猫の手ツール

Algorithm & UI/UX

クッキリした境界を作る「二段階描画」とレトロ配色のロジック

ただ画像を縮小するだけでは「ボケたモザイク」になってしまいます。本ツールがいかにして「ピクセルが際立つドット絵」をフロントエンドのみで生成しているか、その手法を紐解きます。

1. 「ニアレストネイバー法」によるシャープな再拡大

一般的な画像リサイズではバイリニア法などが用いられ、周囲の画素と平均化されるため境界がボケます。本ツールでは、一度 Canvas を極小サイズに縮小して情報を間引いた後、imageSmoothingEnabled = false を設定したメイン Canvas へ描き戻しています。

これにより、拡大時に補間処理が行われず、隣接するピクセルの色がそのまま維持される「ニアレストネイバー(Nearest Neighbor)」効果をブラウザ標準機能だけで実現しています。

// 1. 小さな仮想Canvasを作成して縮小描画(情報の間引き)
const smallCanvas = document.createElement('canvas');
smallCanvas.width = // ユーザー設定に基づいた極小幅;
smallCanvas.height = // ユーザー設定に基づいた極小高;
const sCtx = smallCanvas.getContext('2d');
sCtx.drawImage(originalImage, 0, 0, smallW, smallH);

// 2. メインCanvasの補間を無効化して再拡大
tCtx.imageSmoothingEnabled = false;
tCtx.drawImage(smallCanvas, 0, 0, smallW, smallH, 0, 0, renderW, renderH);

2. 輝度ベースのパレットマッピング(GB風モード)

レトロ感を出すための「GB風」パレットは、単なる色被せではありません。まずピクセルのRGB値からグレースケール輝度を算出し、その輝度をあらかじめ定義した4つの階調(パレット)のいずれかに割り当てる「ディザリングに近い減色処理」を行っています。

この実装により、元の写真の明暗差がドット絵の「色の濃淡」として正しく変換され、昔の携帯ゲーム機のような深みのある表現が可能になります。

// 輝度(Gray)の算出とパレット割り当ての概念
const gbColors = [/* 独自のチューニングカラー4色 */];

for (let i = 0; i < data.length; i += 4) {
    // 心理学的輝度公式を用いたグレースケール化
    const gray = // 独自の係数による計算処理;
    
    // 輝度しきい値に基づいてパレットインデックスを決定
    const idx = gray < 64 ? 0 : gray < 128 ? 1 : gray < 192 ? 2 : 3;
    
    data[i] = gbColors[idx][0];   // R
    data[i+1] = gbColors[idx][1]; // G
    data[i+2] = gbColors[idx][2]; // B
}

3. iPhoneユーザーのための HEIC クライアントサイド変換

iPhoneの標準画像形式であるHEICは、ブラウザのCanvas APIで直接読み込めないケースが多いという課題があります。本ツールでは heic2any ライブラリを活用し、ブラウザ内で JPEG への動的変換を行っています。

これを「サーバーを介さずクライアントサイドで完結」させることで、ユーザーのプライバシーを守りつつ、スマホからの操作性を劇的に向上させています。

// HEIC形式の検知と動的デコード
if (file.name.toLowerCase().match(/\.(heic|heif)$/)) {
    const convertedBlob = await heic2any({
        blob: file,
        toType: "image/jpeg",
        quality: // パフォーマンス重視の圧縮率
    });
    // 以降、通常の画像と同様にCanvasで処理
}

Developer's Note

このツールで一番こだわったのは、「ピクセルの一粒一粒が自立していること」です。単なるフィルター加工ではなく、一度解像度を物理的に落としてから「補間なし」で引き伸ばすという工程を経ることで、当時のゲームソフトのようなソリッドな質感を再現しました。

また、外部サーバーに一切データを送らない設計は、開発者としてのポリシーです。画像処理という重いタスクをブラウザのメモリ(Canvas)だけでやりくりするため、最大解像度の制限やプレビュー用・保存用のCanvasの使い分けなど、見えない部分でのメモリ管理を徹底しています。