猫の手ツール

Security & Canvas Logic

不透明な黒による上書きと座標マッピングの技術

黒塗りは、選んだ範囲を不透明な黒で上書きする処理です。この解説では、表示座標と画像座標の対応、操作前の履歴、保存画像で確認すべき範囲を説明します。

このツールの入力から出力まで

現行コードの確認日:

入力
画像を選び、四角またはペンで隠す範囲を指定します。
処理
操作中の四角はプレビューに描き、指を離した時点で処理Canvasへ不透明な黒を確定します。Undo用の履歴は32MiBの容量予算で管理します。
出力
画像は長辺 2000px 以下に縮小して編集し、JPEG(品質指定 0.95)で保存します。

まず確認する点:半透明の選択表示だけで黒塗りが完了したと判断せず、指を離した後と保存したJPEGで十分な余白まで覆われたか確認します。

実装の確認箇所:src/pages/image/blackout/index.astro。以下のコード抜粋と出力条件は、この本番実装に基づいて説明します。

仕組みを読み進めるときは、メモリ管理と画像処理(画素バッファ、縮小と容量予算)、Canvas APIと画像処理(画像寸法、drawImage、画素と透明度の扱い)が参考になります。

1. 解像度に依存しない動的座標スケーリング

スマホで撮影した写真は3000pxを超える高解像度であることが一般的ですが、ブラウザの画面表示(CSS上のサイズ)は数百px程度です。本ツールでは、表示用のコンテナサイズと、実際のキャンバスピクセルサイズの差異を動的に算出し、タップ位置を正確に元画像のピクセルへマッピングしています。

メリット: ユーザーが画面上で「ここを隠したい」と直感的に操作した位置が、処理画像の座標に換算されます。ただし、指の接触位置や線の端まで自動補正するわけではないため、少し広い範囲を塗って保存画像を確認します。

CSS で縮小表示された Canvas 上のマウス座標を、画像座標へ変換する最小例です。引数 canvas は表示中の Canvas、event は MouseEvent を想定します。

function imagePoint(canvas, event) {
  const rect = canvas.getBoundingClientRect();
  return {
    x: (event.clientX - rect.left) * canvas.width / rect.width,
    y: (event.clientY - rect.top) * canvas.height / rect.height
  };
}

2. メモリ効率を考慮したスタック型履歴管理

Canvas操作において「1つ戻る(Undo)」を実現するには、直前の描画状態(ImageData)をメモリに保持する必要があります。しかし、ImageDataは通常1画素あたり4byteを使うため、画像が大きいほど履歴1枚の容量も増えます。現行実装は32MiBの履歴予算を設け、合計のbyteLengthが予算を超えると古い状態から破棄します。履歴だけの容量制限であり、元画像やほかのCanvasのメモリは別です。

メリット: モバイル端末の限られたメモリ環境でも動作を安定させつつ、ユーザーが試行錯誤できる利便性を提供しています。

3. クライアントサイドでの HEIC 変換

このツールでは、HEICを選んだ場合はブラウザ内で別の画像形式に変換してから編集します。本ツールでは、heic2any ライブラリを動的に読み込み、ブラウザ内でJPEGへ変換するパイプラインを構築しています。

メリット: サーバーサイドに画像を送信して変換する工程を完全に排除。給与明細のような極めて機密性の高い情報を扱うため、画像の変換処理を端末側で行います。ページや変換ライブラリの取得には通信が必要であり、ページ全体が完全オフラインという意味ではありません。

出力の仕様と確認する点

画像は長辺 2000px 以下に縮小して編集し、JPEG(品質指定 0.95)で保存します。選択中の四角は半透明ですが、指を離すと本体に不透明な黒を描きます。ペンは移動した軌跡を描くため、タップだけで塗れたと判断しないでください。

Undo は操作前の ImageData を合計32MiB以内で保持します。枚数は画像の画素数によって変わり、1枚が予算を超える場合はその履歴を追加しません。表示から黒塗り前の情報が消えても、編集中は元画像や履歴がメモリに残ります。元ファイルそのものを削除する機能ではありません。

JPEG の圧縮やペンの境界では周囲の画素が混ざる場合があります。文字・顔を十分な余白ごと隠し、反射、別の箇所に写った同じ情報も確認してください。黒塗り範囲外の情報まで匿名化する機能はありません。