Architecture & Performance
複数Canvas合成と疑似ぼかし
アルゴリズムの最適化手法
ブラウザ上での画像加工は、画素数が増すほど描画量とメモリ使用量が増えます。ユーザー体験を損なわず、かつ滑らかなペン操作を実現するための「レイヤー合成」と「疑似フィルタ」のハックを紐解きます。
このツールの入力から出力まで
現行コードの確認日:
- 入力
- 画像とペン・消しゴムを選び、加工したい範囲をなぞります。
- 処理
- 元画像、加工済み画像、描いたマスクを別に持ちます。requestAnimationFrameで合成をまとめ、マスク内だけモザイクまたはぼかし画像を表示します。
- 出力
- 保存は JPEG(品質指定 0.95)です。
まず確認する点:効果の切替は描いた範囲全体へ適用されます。ブラシで選んだ範囲と、効果の種類を別々の状態として見ると操作を理解しやすくなります。
仕上がりの例
猫の写真に写ったパソコン画面だけをぼかした例です。
掲載画像はサイトにある既存の公開作例です。保存済みの資料では運営者の愛猫の写真を使用しています。今回の確認日が、この作例を実行した日を表すわけではありません。
保存済み検証記録の条件を見る
資料の種類は「既存の公開作例」です。公開作例と保存済み記録で素材や色などが異なる場合があるため、下の数値は保存済み検証記録の条件として読んでください。
「モザイク」を選び、ノートパソコンの画面をブラシでなぞって加工した例です。隣の猫やキーボードを残しながら、画面の表示内容にモザイクをかけています。
- 記録の入力画像
- 800×839px (JPEG)
- 記録の出力画像
- 800×839px (JPEG)
この作例には操作値の記録がないため、同じ設定で再現できるとは断定しません。本文の数値は現行の実装仕様です。
資料ファイルの作成日は実行日として扱っていません。個別端末の処理速度や、ほかの写真に対する検出・加工の精度を測定した資料でもありません。
実装の確認箇所:src/pages/image/blur-brush/index.astro。以下のコード抜粋と出力条件は、この本番実装に基づいて説明します。
仕組みを読み進めるときは、イベントリスナーの基礎(タップやドラッグから描画へつなぐ流れ)、Canvas APIと画像処理(画像寸法、drawImage、画素と透明度の扱い)が参考になります。
1. destination-in モードを用いた非破壊レイヤー合成
このツールの核心は、元の画像を直接書き換えるのではなく、5枚のCanvas(元画像、モザイク用、ぼかし用、マスク用、合成一時用)をレイヤーとして管理している点にあります。ユーザーがなぞった軌跡を「マスク」として保持し、globalCompositeOperation = 'destination-in' を使用してエフェクト層を型抜きしています。
メリット: 消しゴム機能の実装が「マスク層を透明にするだけ」で完結します。また、モザイクとぼかしを切り替える際も、既に描いたマスクを流用できるため、再描画のコストが最小限で済みます。
effect と mask を同じサイズの Canvas として用意した場合の型抜き例です。白など不透明なマスク部分だけが残ります。
function maskedEffect(effect, mask) {
const canvas = document.createElement('canvas');
canvas.width = effect.width;
canvas.height = effect.height;
const ctx = canvas.getContext('2d');
if (!ctx) throw new Error('Canvas unavailable');
ctx.drawImage(effect, 0, 0);
ctx.globalCompositeOperation = 'destination-in';
ctx.drawImage(mask, 0, 0);
return canvas;
} 2. 縮小・拡大による低コストな「疑似ぼかし」ハック
ブラウザ標準の filter: blur() は、動的に変化する描画に対しては計算負荷が非常に高く、スマホではカクつきの原因になります。本ツールでは、画像を一度極小サイズに縮小し、imageSmoothingEnabled = true(滑らかな補完)を有効にした状態で元のサイズに拡大し直すことで「疑似ぼかし」を生成しています。
メリット: ピクセルごとの重い畳み込み演算を回避し、縮小画像の補間を利用してぼかし済みレイヤーを用意します。処理時間は端末と画像サイズによって異なります。
3. requestAnimationFrame による描画頻度の制御
高頻度で発生する touchmove イベントごとに Canvas の合成処理を走らせると、ブラウザの描画パイプラインが飽和します。本ツールでは、イベント発生時は「フラグを立てる」だけにとどめ、ディスプレイの更新タイミング(requestAnimationFrame)に合わせて描画を統合しています。
メリット: 入力イベントと描画更新を分離することで、同じフレーム内の合成要求をまとめます。描画処理そのものはメインスレッドで実行するため、遅延をなくす保証ではありません。
出力の仕様と確認する点
長辺 1500px 以下の画像を使い、モザイクは縦横を 2.5% に縮小後、補間なしで拡大します。ぼかしは 3% に縮小後、補間ありで拡大する固定の処理です。ガウスぼかしと同じ計算ではありません。
ペンでマスクを追加し、消しゴムでその範囲を除きます。モザイクとぼかしを切り替えると、描いた範囲全体の効果が切り替わります。異なる効果を別々の範囲に固定して蓄積する機能ではありません。
保存は JPEG(品質指定 0.95)です。ぼかし・モザイク後も色や大きな形は残ります。重要な文字や顔を隠す用途では、加工後の実画像で読めないことを確認してください。
