HTML5 canvasのピクセル操作

HTML5 canvas APIには、canvas上のピクセルを取得するgetImageData()、canvas上のピクセルを書き換えるputImageData()、ピクセルデータを生成するcreateImageData()が用意されており、簡単にcanvas上のピクセルにアクセスすることができます。

ピクセルデータを取得する getImageData()

getImageData()は、canvasのピクセルデータをImageDataオブジェクトで返します。第1, 2引数でピクセルデータを取得する領域の(x, y)座標、第3, 4引数で幅と高さを指定します。

取得したImageDataオブジェクトは、widthプロパティに幅、heightプロパティに高さ、dataプロパティにCanvasPixelArrayオブジェクトを持っています。CanvasPixelArrayオブジェクトは、配列に似た形式で各ピクセルの色情報を格納しています。

下記の例では、座標(0, 0)に赤、(1, 0)に緑、(0, 1)に青の縦横1pxの矩形を描画し、canvasの左上から縦横2pxのピクセルデータを取得しています。

JavaScript

window.onload = function() {
  function drawPixel(context, x, y, color) {
    context.save();
    context.translate(x, y);
    context.fillStyle = color;
    context.fillRect(0, 0, 1, 1);
    context.restore();
  }
  var canvas = document.getElementById('my-canvas');
  var context = canvas.getContext('2d');
  drawPixel(context, 0, 0, 'rgba(255, 0, 0, 1)');
  drawPixel(context, 1, 0, 'rgba(0, 255, 0, 1)');
  drawPixel(context, 0, 1, 'rgba(0, 0, 255, 1)');
  var imageData = context.getImageData(0, 0, 2, 2);
  console.log(imageData.width); // 出力:2
  console.log(imageData.height); // 出力:2
  console.log(imageData.data); // 出力:CanvasPixelArray
};

下図は、コンソールのキャプチャに情報を書き加えたものです。CanvasPixelArrayオブジェクトを見ると配列に似た形式で各ピクセルの色情報が格納されていることがわかります。各ピクセルは、r, g, b, aの4つの成分に分けられ、左から右、上から下の順番に格納されます。また、右下の何も描画していないピクセルは透明な黒となっていることがわかります。

ピクセルを書き換える putImageData()

canvas上のピクセルは、putImageData()で書き換えることができます。putImageData()は、第1引数でImageDataオブジェクト、第2, 3引数でピクセルを書き換えるcanvas上の座標(x, y)を指定します。また、オプションの第4, 5, 6, 7引数で書き換えを行うImageData上の領域(x, y, width, height)を指定することができます。

下記の例では、まずcanvasの中央に縦横200pxの矩形を赤で描画し、その中央の縦横100pxの領域をgetImageData()で取得しています。次に取得したImageDataの各ピクセルのalpha値を半透明に変更し、最後にputImageData()でcanvas上のピクセルを書き換えています。

JavaScript

window.onload = function() {
  function setPixel(imageData, x, y, color) {
    var width = imageData.width;
    var data = imageData.data;
    var index = ((width * y) + x) * 4;
    if (!isNaN(color.r)) {
      data[index] = color.r;
    }
    if (!isNaN(color.g)) {
      data[index + 1] = color.g;
    }
    if (!isNaN(color.b)) {
      data[index + 2] = color.b;
    }
    if (!isNaN(color.a)) {
      data[index + 3] = color.a;
    }
  }
  var canvas = document.getElementById('my-canvas');
  var context = canvas.getContext('2d');
  var center = {x:canvas.width * 0.5, y:canvas.height * 0.5};
  context.save();
  context.translate(center.x, center.y);
  context.fillStyle = 'rgb(255, 0, 0)';
  context.fillRect(-100, -100, 200, 200);
  context.restore();
  var imageData = context.getImageData(center.x - 50, center.y - 50, 100, 100);
  var color = {r:NaN, g:NaN, b:NaN, a:128};
  var width = imageData.width;
  var height = imageData.height;
  for (var x = 0; x < width; ++x) {
    for (var y = 0; y < height; ++y) {
      setPixel(imageData, x, y, color);
    }
  }
  context.putImageData(imageData, center.x - 50, center.y - 50);
};

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

createImageData()を使うと新規のImageDataオブジェクトを作成することができます。第1, 2引数で幅と高さを渡した場合は、指定したサイズのImageDataオブジェクトが返されます。また、createImageData()の引数にImageDataオブジェクトを渡した場合は、渡したImageDataと同じサイズで初期化されます。作成されたImageDataは、すべてのピクセルが透明な黒となります。

下記の例では、createImageData()でcanvasと同じサイズのImageDataを作成し、各ピクセルを操作してノイズを生成した後、putImageData()でcanvas上のピクセルを書き換えています。

JavaScript

window.onload = function() {
  var canvas = document.getElementById('my-canvas');
  var context = canvas.getContext('2d');
  var imageData = context.createImageData(canvas.width, canvas.height);
  var data = imageData.data;
  var length = data.length;
  var maxValue = 255;
  // 色の強さ 1〜255
  var strength = 64;
  // ノイズの量 0〜1
  var quantity = 0.2;
  for (var i = 0; i < length; ++i) {
    if (i % 4 == 3) {
      data[i] = maxValue;
    } else {
      if (Math.random() < quantity) {
        data[i] = Math.floor(Math.random() * strength) + (maxValue - strength);
      } else {
        data[i] = maxValue;
      }
    }
  }
  context.putImageData(imageData, 0, 0);
};