Canvas とピクセル操作

これまで、canvas の実際のピクセルは見てきませんでした。ImageData オブジェクトを使用して、ピクセルデータを操作するためにデータ配列へ直接読み取りや書き込みを行うことが可能です。また、画像のスムージング (アンチエイリアシング) の制御方法や canvas の画像を保存する方法も見ていきます。

ImageData オブジェクト

ImageData オブジェクトは、canvas オブジェクトの領域にあるピクセルデータを表します。これは以下の読み取り専用プロパティを持ちます:

width
画像の幅をピクセル数で表します。
height
画像の高さをピクセル数で表します。
data
0 から 255 の間の (両端の値を含む) 整数データを RGBA の順で収めた一次元配列を表す Uint8ClampedArray です。

data プロパティは、生のピクセルデータを参照するためにアクセス可能な Uint8ClampedArray を返します。それぞれのピクセルは 4 つの 1 バイト値 (赤、緑、青、アルファの順、すなわち "RGBA" 形式) で表します。また、それぞれの色成分は 0 から 255 の間の整数で表します。さらに、それぞれの成分は配列内で連続した添字が割り当てられており、左上のピクセルの赤色成分が配列の添え字 0 になります。配列の中でピクセルは左から右へ進み、さらに下へと進んでいきます。

Uint8ClampedArrayheight × width × 4 バイトのデータがあり、添字の範囲は 0 から (height×width×4)-1 になります。

例えば画像の 50 行目の 200 列目にあるピクセルから青色成分の値を読み取るには、以下のようにします:

blueComponent = imageData.data[((50*(imageData.width*4)) + (200*4)) + 2];

Uint8ClampedArray.length 属性を読み取ると、ピクセル配列のサイズをバイト数で知ることができます:

var numBytes = imageData.data.length;

ImageData オブジェクトを作成する

新たに空の ImageData オブジェクトを作成するには、createImageData() メソッドを使用します。createImageData() メソッドは 2 種類の形式があります:

var myImageData = ctx.createImageData(width, height);

これは、特定の寸法の新たな ImageData オブジェクトを作成します。すべてのピクセルは透明な黒色に設定されます。

anotherImageData で指定したオブジェクトと同じ寸法の、新たな ImageData オブジェクトを作成することもできます。新しいオブジェクトのピクセルは、すべて透明な黒色に設定されます。画像データはコピーされません!

var myImageData = ctx.createImageData(anotherImageData);

コンテキストのピクセルデータを取得する

canvas コンテキストのピクセルデータの複製を持つ ImageData オブジェクトを取得するには、getImageData() メソッドを使用します:

var myImageData = ctx.getImageData(left, top, width, height);

このメソッドは (left,top)、(left+width, top)、(left, top+height)、(left+width, top+height) の点で四隅を表した canvas の領域のピクセルデータを表す ImageData オブジェクトを返します。点の座標は、canvas の座標空間の単位で指定します。

注記: 返される ImageData オブジェクトで、canvas の外部にあるピクセルはすべて透明な黒色になります。

このメソッドは、Manipulating video using canvas の記事でも説明しています。

カラーピッカー

この例では、マウスカーソルの下にある色を表示するために getImageData() メソッドを使用しています。ここでは現在のマウスカーソルの位置を layerXlayerY で求めて、getImageData() が提供するピクセル配列で該当位置のピクセルデータを探します。最後に、色を表示するための <div> で背景色とテキストを設定するために、配列データを使用します。

var img = new Image();
img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
img.onload = function() {
  ctx.drawImage(img, 0, 0);
  img.style.display = 'none';
};
var color = document.getElementById('color');
function pick(event) {
  var x = event.layerX;
  var y = event.layerY;
  var pixel = ctx.getImageData(x, y, 1, 1);
  var data = pixel.data;
  var rgba = 'rgba(' + data[0] + ',' + data[1] +
             ',' + data[2] + ',' + (data[3] / 255) + ')';
  color.style.background =  rgba;
  color.textContent = rgba;
}
canvas.addEventListener('mousemove', pick);

コンテキストにピクセルデータを描く

putImageData() メソッドを使用して、コンテキストにピクセルデータを描くことができます:

ctx.putImageData(myImageData, dx, dy);

引数 dxdy は、描画したいピクセルデータの左上の隅を描く位置を、コンテキストのデバイス座標で示します。

例えば myImageData が表す画像全体をコンテキストの左上の隅から描くには、単純に以下のようにします:

ctx.putImageData(myImageData, 0, 0);

色のグレースケール化と反転

この例ではすべてのピクセルの値を変更するためにイテレートを行って、putImageData() を使用して変更後のピクセル配列を canvas に書き戻しています。invert 関数は、単純に最大値の 255 からそれぞれの色の値を減算します。grayscale 関数は、単純に赤、緑、青の平均値を使用します。また、例えば x = 0.299r + 0.587g + 0.114b といった式による加重平均も使用できます。詳しくは Wikipedia の Grayscale (日本語版) をご覧ください。

var img = new Image();
img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
img.onload = function() {
  draw(this);
};

function draw(img) {
  var canvas = document.getElementById('canvas');
  var ctx = canvas.getContext('2d');
  ctx.drawImage(img, 0, 0);
  img.style.display = 'none';
  var imageData = ctx.getImageData(0,0,canvas.width, canvas.height);
  var data = imageData.data;
    
  var invert = function() {
    for (var i = 0; i < data.length; i += 4) {
      data[i]     = 255 - data[i];     // red
      data[i + 1] = 255 - data[i + 1]; // green
      data[i + 2] = 255 - data[i + 2]; // blue
    }
    ctx.putImageData(imageData, 0, 0);
  };

  var grayscale = function() {
    for (var i = 0; i < data.length; i += 4) {
      var avg = (data[i] + data[i +1] + data[i +2]) / 3;
      data[i]     = avg; // red
      data[i + 1] = avg; // green
      data[i + 2] = avg; // blue
    }
    ctx.putImageData(imageData, 0, 0);
  };

  var invertbtn = document.getElementById('invertbtn');
  invertbtn.addEventListener('click', invert);
  var grayscalebtn = document.getElementById('grayscalebtn');
  grayscalebtn.addEventListener('click', grayscale);
}

ズームとアンチエイリアシング

drawImage() メソッド、第 2 の canvas、imageSmoothingEnabled プロパティの力を借りて、画像をズームアップして詳しく見ることができます。

マウスカーソルの位置を取得して、そこから上下左右に 5 ピクセルの範囲の画像を切り取ります。そして切り取った画像を別の canvas にコピーして、望むサイズにリサイズします。ズーム用の canvas では、元の canvas から切り取った 10×10 ピクセルの画像を 200×200 ピクセルにリサイズしています。

zoomctx.drawImage(canvas, 
                  Math.abs(x - 5), Math.abs(y - 5),
                  10, 10, 0, 0, 200, 200);

アンチエイリアシングはデフォルトで有効ですので、ピクセルをはっきりさせるためにスムージングを無効化したいと考えるかもしれません。チェックボックスを切り替えると、imageSmoothingEnabled プロパティ (さまざまなブラウザ向けに接頭辞が必要です) の効果を確認できます。

var img = new Image();
img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
img.onload = function() {
  draw(this);
};

function draw(img) {
  var canvas = document.getElementById('canvas');
  var ctx = canvas.getContext('2d');
  ctx.drawImage(img, 0, 0);
  img.style.display = 'none';
  var zoomctx = document.getElementById('zoom').getContext('2d');
 
  var smoothbtn = document.getElementById('smoothbtn');
  var toggleSmoothing = function(event) {
    zoomctx.imageSmoothingEnabled = this.checked;
    zoomctx.mozImageSmoothingEnabled = this.checked;
    zoomctx.webkitImageSmoothingEnabled = this.checked;
    zoomctx.msImageSmoothingEnabled = this.checked;
  };
  smoothbtn.addEventListener('change', toggleSmoothing);

  var zoom = function(event) {
    var x = event.layerX;
    var y = event.layerY;
    zoomctx.drawImage(canvas,
                      Math.abs(x - 5),
                      Math.abs(y - 5),
                      10, 10,
                      0, 0,
                      200, 200);
  };

  canvas.addEventListener('mousemove', zoom);
}

画像を保存する

HTMLCanvasElement は、画像を保存する際に役に立つ toDataURL() メソッドを提供します。これは、引数 type で指定した形式 (既定値は PNG) で表した画像を持つ data URI を返します。返される画像の解像度は 96 dpi です。

canvas.toDataURL('image/png')
既定の設定。PNG 画像を作成します。
canvas.toDataURL('image/jpeg', quality)
JPG 画像を作成します。オプションで、品質を 0 から 1 の範囲で指定できます。1 は最高品質、0 はほとんど見分けがつかなくなりますがファイルサイズを小さくできます。

canvas から生成した data URI は、例えば任意の <image> のソースとして使用したり、ディスクに保存するために download 属性を持つハイパーリンクに投入することができます。

また、canvas から Blob を生成することもできます。

canvas.toBlob(callback, type, encoderOptions)
canvas に含まれる画像を表す Blob オブジェクトを作成します。

関連情報

ドキュメントのタグと貢献者

 このページの貢献者: yyss
 最終更新者: yyss,