読者です 読者をやめる 読者になる 読者になる

canvasをバイト単位で修正する方法(ImageDataの使い方)

引っ越ししました。agoです。

思いっきりネタがかぶってますが、あまり気にせずcanvasネタを書いてみたいと思います。

今回はcanvasの中でもImageData関係をまとめて見ました。

ImageDataってなに?

canvas内のバイト列を扱うためのObjectです。

canvas内の各バイト毎に赤、緑、青、透明度の情報を配列として保持しています。

何に使うの?

canvas内をバイト単位で修正したい場合に使います。

canvas内に図形等を書く場合、通常提供されているlineTo等を使用することも出来ますが、こういった抽象メソッドは一回ごとの呼び出しコストが大きいため、細かい単位の操作には向きません。

その点、ImageDataであればバイト単位での操作しかできない代わりに呼び出し毎のコストが小さいため、細かい単位の操作も高速に行うことができます。

ただ、もちろん線を引く、丸を書く等の標準で提供されている方法は、そのものずばりのメソッドを使用する方が高速です。

どうやって作成するの?

空のImageDataを作成するには以下のようにcreateImageDataを使用します。

var width = 100;
var height = 100;
var cc = canvas.getContext('2d');
var ImageData = cc.createImageData(width, height);

これでImageDataには100 * 100の透明なImageDataが作成されます。

既存のcanvasの中身を変更したい。

既存のcanvasを変更したい場合、以下のようにgetImageDataで書き換えたい場所のpixelを取得し、ImageData.dataを書き換えた後、putImageDataでcanvasへ書き出します。

var target_pixel = 0; // 黒くしたいpixelの場所 
var cc = canvas.getContext('2d');
var top = 0;
var left = 0;
var width = canvas.width;
var height = canvas.height;
// canvas全体をImageDataとして取得 
var ImageData = cc.getImageData(top, left, width, height);
// target_pixelの書き換え 
ImageData.data[target_pixel + 0] = 255; // 赤 red 
ImageData.data[target_pixel + 1] = 255; // 緑 green 
ImageData.data[target_pixel + 2] = 255; // 青 blue 
ImageData.data[target_pixel + 3] = 255; // 透明度 alpha 
// 書き換えたImageDataをcanvasへ書き出す 
cc.putImageData(ImageData, top, left);

全体の色を書き換えたい。

色を変更したい場合ImageData.dataを書き換えますが、この配列はcanvas内の縦、横、色情報を保持した1次元配列(CanvasPixelArrayオブジェクト)になっています。
そのため、ある一定範囲のpixelを繰り返し処理したい場合、以下のようなループを行う必要があります。

var cc = canvas.getContext('2d');
var top = 0;
var left = 0;
var width = 255;
var height = 255;
var ImageData = cc.getImageData(top, left, width, height);
var data = ImageData;
for (var x = 0; x < width; ++x) {
    for (var y = 0; y < height; ++y) {
        // 対象pixelのindex(色情報があるので*4する) 
        var index = (x + y * width) * 4;
        data[index + 0] = x;
        data[index + 1] = y;
        data[index + 2] = (x + y) / 255;
        data[index + 3] = 255;
    };
};
ImageData.data = data;
cc.putImageData(ImageData, top, left);

使ってみた感想

一定範囲を操作する場合、一次元配列に色情報が入っているので若干癖がありますが、それ以外は普通の配列操作なのでそこまで特殊な処理は必要ありません。

おまけ

一応何か例題をと思い、勉強がてらwonderflで公開されているパーティクル崩しJSへ移植してみました

safari、chromeではある程度動作していますが(それでも若干おかしいですが)、Fxではかなり動きがおかしいので注意してください。

2010/5/17 追記
Firefox、Operaでも動くバージョンを作った方がいらっしゃったので紹介させていただきます。
JSパーティクル崩し - 0xFF

カヤックでは絵の描ける技術者を募集しています!