2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【JavaScript】Canvasの基本【HTML5】

Last updated at Posted at 2020-11-10

#はじめに
昔自分で書いたブログ「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は存在するが、それ以外の画像処理を行う場合
画像バッファを直接触ることになる

image2.png

#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
2
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?