2014/01/23

[JavaScript]canvasのdrawImage()とputImageData()を比べてみました。

PixelArtsという個人的なプロジェクトでcanvasをガンガン使っているんですが、canvasとその周辺技術についてTipsをまとめたいと思います。canvasを使って、複雑な描画をしようとすると、canvasが1枚では足りない状況に陥ります。その場合はゲームなどでよく使われるように別に一時描画用のcanvasを用意して予め画面を作って一気にコピーすると色々捗ります。その際に利用するメソッドは、drawImage()か、はたまたputImageData()なのか。どっちでも良いわけじゃなかった、2つの違いを比べてみました。




drawImage()とputImageData()の違い

drawImage()とputImageData()の違いは、昔はコピーによる画像の劣化が指摘されていました(こちらのサイトで詳しく調べられている方がおられます)が、今は特に考えなくてもいいような気がします。それより何より、この2つは両方共異なるcanvas間で画像をコピーするのですが、コピーする内容が微妙に違うので、用途に合わせて使い分けする事で表現の幅が広がるように思います。

putImageData()はgetImageData()で取得したりcreateImageData()で作成したイメージデータ(=imagedata)をcanvasに配置する訳ですが、このイメージデータの内容は配列になっていて(imagedata.data)、この配列を直接書き換えるメソッドです。
一方drawImage()はcanvas上の指定した短形のデータをコピーしますが、コピー元の透過情報をコピー先に適用するので、何も描画していないコピー元の部分はコピー先に描画されているものが表示されます。つまりは画像処理ソフトでいうレイヤーのように利用できるわけです。
以下はサンプルです。右のcanvasはコピー先で左のcanvasがコピー元です。また赤の円はdrawImage()でコピーしたもので、緑の円はputImageData()でコピーしたものになります。




ソースは以下の通りです。
function test3(){
    var canvas1 = document.getElementById('test3');
    var ctx1 = canvas1.getContext('2d');
    var canvas2 = document.createElement('canvas');    // バッファリング用の表示しないcanvas
    var ctx2 = canvas2.getContext('2d');
    var btn = document.getElementById('btn3');
    // canvas 2D contextが使える前提

    canvas1.width = 300;
    canvas1.height = 300;
    canvas2.width = 300;
    canvas2.height = 300;
    ctx1.fillStyle = '#000000';
    ctx1.fillRect(0, 0, 300, 300);
    ctx1.fill();
    draw();

    function draw(){
        ctx2.beginPath();
        ctx2.fillStyle = '#ff0000';
        ctx2.arc(100, 100, 20, 0, Math.PI * 2, false);
        ctx2.fill();
        ctx2.beginPath();
        ctx2.fillStyle = '#00ff00';
        ctx2.arc(200, 200, 20, 0, Math.PI * 2, false);
        ctx2.fill();

        ctx1.drawImage(canvas2, 80, 80, 40, 40, 80, 80, 40, 40);
        var imagedata = ctx2.getImageData(180, 180, 40, 40);
        ctx1.putImageData(imagedata, 180, 180);
    };
};

canvasを複数枚使うというのは、処理がややこしくなりますし、最初はあまりメリットを感じないですが、描画する点数が多かったり、同じ画像をローテーションして何回か使ったりする場合は利用価値があるように思います。
私は、パーツ毎にバッファ用のcanvasに描画しておいて、最終的にdrawImage()で表示するcanvasにコピーするという方法をよく使います。

また、合成の方法描画した時の合成処理の方法をglobalCompositeOperationプロパティで指定する事で、マスク処理のように合成したり加算処理したりもできます。 → globalCompositeOperation プロパティ - Canvasリファレンス - HTML5.JP

画像処理に関してはマシンパワーを使わず、高速で高品質に描画する方法を日々模索している、といった感じです。

PixelArtsはMath.random()を使ってプログラマーが芸術に挑戦するというものです。
PixelArtsの方もよろしくお願いします。

PixelArts


0 件のコメント:

コメントを投稿