HTML5 Canvas上で低コスト＆ローカル動作可能なぼかし(Blur)表現

#概要
##背景
Canvas上でぼかし(Blur)を行えるJSライブラリは数多くありますが、どれもピクセル処理をするため高品質であるものの高負荷なので、スマートフォン向けにアクションゲームを作るような状況だと採用しづらいです。
また、ピクセル処理をする関係で、ローカル動作だとブラウザによってセキュリティ(同一出身ポリシー)に阻まれちゃんと動作しません。

参考：
[canvas の getImageDataが少しめんどくさい（特にローカルで動かす場合）](http://d.hatena.ne.jp/chiheisen/20100815/1281885412)
[JavaScriptにおけるローカルファイルアクセス権限のポリシー](http://feather.cocolog-nifty.com/weblog/2010/05/javascript-e781.html)

一方、CSSでCanvas要素をblurでぼかすことはできますが、こちらは内部処理用に作成したCanvasには効きませんし、ぼかし有りCanvasをぼかし無しCanvasに合成するとぼかしが反映されません。

##それでもやっぱりぼかし表現をやりたい
多少品質が犠牲になりますが
- ピクセル処理をしない
- CSSを使わない
- 低負荷
- ローカル環境でも動作する

を可能にするぼかし表現をご紹介します。

# 実装
## Canvasにはアンチエイリアス機能が備わっている
Canvasに画像を表示する際、1.0〜0.5倍で縮小表示すれば2x2サンプリングでアンチエイリアスが掛かるようです。
0.5倍を下回るとサンプリングの都合でギザギザします。
参考：
[Html5 canvas drawImage: how to apply antialiasing](http://stackoverflow.com/questions/17861447/html5-canvas-drawimage-how-to-apply-antialiasing)

## アンチエイリアスを逆手に取る
小さく描いたものを拡大すればぼかし出力が得られそうです。
また、2倍までの拡大で抑えればそこそこなクオリティを維持できそうです。

## 小さく描いて拡大する
読み込んだ画像にぼかした文字を付加して表示してみます。

```html
<!DOCTYPE html>
<html>
<body>
<canvas></canvas>
<script>
    var img = new Image();
    img.src = 'sample.jpg';
    img.onload = function() {
        var canvas = document.getElementsByTagName('canvas')[0];
        canvas.width = img.width;
        canvas.height = img.height;
        var text = 'Blur Effect';

        // 0.5倍, 0.25倍の内部キャンバス生成
        var canvasBlur050 = document.createElement("canvas");
        canvasBlur050.width = canvas.width * 0.5;
        canvasBlur050.height = canvas.height * 0.5;
        var canvasBlur025 = document.createElement("canvas");
        canvasBlur025.width = canvas.width * 0.25;
        canvasBlur025.height = canvas.height * 0.25;

        // 普通に画像を表示
        var context = canvas.getContext('2d');
        context.drawImage(img, 0, 0);
        context.fillStyle = '#FFFF00';
        context.strokeStyle = '#FF0000';
        context.font = '64px Arial';
        context.fillText(text, 50, canvas.height * 0.33);
        context.strokeText(text, 50, canvas.height * 0.33);

        // 0.25倍の内部キャンバスに文字描画
        var contextBlur025 = canvasBlur025.getContext('2d');
        contextBlur025.save();
        contextBlur025.setTransform(0.25, 0, 0, 0.25, 0, 0);
        contextBlur025.fillStyle = '#FFFF00';
        contextBlur025.strokeStyle = '#FF0000';
        contextBlur025.font = '64px Arial';
        contextBlur025.fillText(text, 50, canvas.height * 0.66);
        contextBlur025.strokeText(text, 50, canvas.height * 0.66);
        contextBlur025.restore();

        // 0.5倍の内部キャンバスに拡大
        var contextBlur050 = canvasBlur050.getContext('2d');
        contextBlur050.drawImage(canvasBlur025, 0, 0, canvasBlur025.width, canvasBlur025.height, 0, 0, canvasBlur050.width, canvasBlur050.height);

        // 元のキャンバスに拡大して合成
        context.drawImage(canvasBlur050, 0, 0, canvasBlur050.width, canvasBlur050.height, 0, 0, canvas.width, canvas.height);
</script>
</body>
</html>
```

#結果
![output.png](https://qiita-image-store.s3.amazonaws.com/0/58327/a1cd488e-76ab-5342-9bf2-d4ccc0d700fc.png)
ちょっと粗い気もしますが、リアルタイム処理でこのクオリティなら問題ないでしょう。

##応用
- [いらすとや 雲が浮かぶ青空のイラスト（背景素材）](http://www.irasutoya.com/2016/01/blog-post_656.html)
- [いらすとや 梅の開花のイラスト](http://www.irasutoya.com/2015/02/blog-post_326.html)
- [いらすとや ニコラ・テスラの似顔絵イラスト](http://www.irasutoya.com/2015/11/blog-post_858.html)

を使用して被写界深度のような表現
![canvas.png](https://qiita-image-store.s3.amazonaws.com/0/58327/f1a8de11-b7b0-968d-d03b-e4129a1c2381.png)
Canvas倍率は遠景0.4、人物1.0、近景0.25です。

- ぼかし方向が要素の中央からではなく左上からなので重ねる際に注意
- ぼかし具合を動的に変化させるには手間がかかる（毎回内部Canvasサイズを変えないといけない）
- ぼかしが粗めなので負荷とのトレードオフの選択肢として