#はじめに
昔自分で書いたブログ「Canvasメモ」の転送です。
6,7年前に書いたので情報古いかもしれません。
#画像を読み込んでcanvas表示するまでの普通の流れ
$(function() {
var image = new Image();
image.onload = onload;
image.src = "image/image1.png"; // 画像パス
function onload() {
var canvas = document.getElementsByTagName('canvas')[0],
context, imageData;
// キャンバスサイズを画像サイズに合わせる
canvas.width = image.width;
canvas.height = image.height;
// コンテキストを取得
context = canvas.getContext('2d');
// 画像をコンテキストに描画
context.drawImage(image, 0, 0);
///////////////////////////////////
// 以降は画像を加工する場合
///////////////////////////////////
// 画像のバッファを取得(バッファはimageData.dataである)
// imageData = context.getImageData(0, 0, canvas.width, canvas.height);
// 画像加工プログラムをここに書く(画像にモザイク処理を施すなど)
// 加工した画像を貼り付ける
// context.putImageData(imageData, 0, 0);
}
});
#画像のバッファについて
1pixelの色情報を保持するのに1byte(0~255)×4を必要とする。
4byteはRGBAに対応している。
例えば、1,000×1,000の画像のメモリ使用量は、4×1,000×1,000byte(約4MB)である。
画像の原点は左上であり、 幅がw, 高さがhの画像の(m,n)のピクセルの色情報は、
画像バッファをbufferとすると
R: buffer[(n * w + m) * 4]
G: buffer[(n * w + m) * 4 + 1]
B: buffer[(n * w + m) * 4 + 2]
A: buffer[(n * w + m) * 4 + 3]
である。
直線や曲線を描画するAPIは存在するが、それ以外の画像処理を行う場合
画像バッファを直接触ることになる
#canvasのwidthとheightについて
<canvas width="480" height="320"></canvas>
canvas.width,canvas.heightは画像のバッファサイズである。
canvas.style.width,canvas.style.heightは見た目のサイズである。
よくわからない場合はcanvas.width,canvas.heightのみ設定すると良い。
尚、canvas.width,canvas.heightにはpxは不要である。
#context.drawImage()の引数について
引数は3パターンあり、よく忘れるためメモしておく。
context.drawImage(image, dx, dy)
context.drawImage(image, dx, dy, dw, dh)
context.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
#context.getImageData()について
getとあるが、この関数は呼ばれる度にバッファを作成している。
その為、本メソッドは遅い。
また、戻り値をそのまま比較することは意味がない。
context.getImageData(0, 0, 100, 100) === context.getImageData(0, 0, 100, 100) // false
#ImageDataについて
[Constructor(unsigned long sw, unsigned long sh),
Constructor(Uint8ClampedArray data, unsigned long sw, optional unsigned long sh),
Exposed=(Window,Worker)]
interface ImageData {
readonly attribute unsigned long width;
readonly attribute unsigned long height;
readonly attribute Uint8ClampedArray data;
};
プロパティ全て(width,height,data)がreadonlyである為、バッファを保持するためには
ImageDataまるごと保持しなくてはならない。
dataもreadonlyであるが、配列の参照を変更できないのであって、配列の中身は変更して構わない。
###※ImageDataのコンストラクタは使えない。
以下のように定義すれば、第一引数がUint8ClampedArrayの時のheightがoptionalを除けば使える。
今のところはcontextからcreateImageDataするしかなさそうだ。
function ImageData() {
var i = 0;
if(arguments[0] instanceof Uint8ClampedArray) {
var data = arguments[i++];
}
var width = arguments[i++];
var height = arguments[i];
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
var imageData = ctx.createImageData(width, height);
if(data) imageData.data.set(data);
return imageData;
}
var imgData1 = new ImageData(data, width, height);
var imgData2 = new ImageData(width, height);
#drawImage と putImageDataの速度比較
以下のプログラムをで測定を実施。
drawImage()速度測定プログラム
var startTime = new Date();
for(var i = 0; i < 500; i += 1) {
context.drawImage(image, 0, 0);
}
var endTime = new Date();
$('#timeSpan').html("context.drawImage is " + (endTime - startTime) + ' ms');
putImageData()速度測定プログラム
var startTime = new Date();
for(var i = 0; i < 500; i += 1) {
context.putImageData(imageData, 0, 0);
}
var endTime = new Date();
$('#timeSpan').html("context.putImageData is " + (endTime - startTime) + ' ms');
結果(500回の実行にかかった時間):
drawImage: 10ms
putImageData: 1900ms
drawImageが想像以上に速い。putImageDataが結構遅い。
#Uint8Array と Uint8ClampedArrayの違い
0から255の範囲外の任意の値をクランプされた配列に1要素を設定しようとしている場合、
それは単に(値が小さいか大きいかどうかに依存します)0または255にデフォルト設定されます。
通常のUint8Arrayはちょうど取る値の最初の8ビットが設定されます。
var x = new Uint8ClampedArray([17, -45.3]);
console.log(x[0]); // 17
console.log(x[1]); // 0
console.log(x.length); // 2
var x = new Uint8Array([17, -45.3]);
console.log(x[0]); // 17
console.log(x[1]); // 211
console.log(x.length); // 2