これまで、canvas の実際のピクセルは見てきませんでした。ImageData
オブジェクトを使用して、ピクセルデータを操作するためにデータ配列へ直接読み取りや書き込みを行うことが可能です。また、画像のスムージング (アンチエイリアシング) の制御方法や canvas の画像を保存する方法も見ていきます。
ImageData
オブジェクト
ImageData
オブジェクトは、canvas オブジェクトの領域にあるピクセルデータを表します。これは以下の読み取り専用プロパティを持ちます:
width
- 画像の幅をピクセル数で表します。
height
- 画像の高さをピクセル数で表します。
data
0
から255
の間の (両端の値を含む) 整数データを RGBA の順で収めた一次元配列を表すUint8ClampedArray
です。
data
プロパティは、生のピクセルデータを参照するためにアクセス可能な Uint8ClampedArray
を返します。それぞれのピクセルは 4 つの 1 バイト値 (赤、緑、青、アルファの順、すなわち "RGBA" 形式) で表します。また、それぞれの色成分は 0 から 255 の間の整数で表します。さらに、それぞれの成分は配列内で連続した添字が割り当てられており、左上のピクセルの赤色成分が配列の添え字 0 になります。配列の中でピクセルは左から右へ進み、さらに下へと進んでいきます。
Uint8ClampedArray
は height
× 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() メソッドを使用しています。ここでは現在のマウスカーソルの位置を layerX
と layerY
で求めて、getImageData() が提供するピクセル配列で該当位置のピクセルデータを探します。最後に、色を表示するための <div>
で背景色とテキストを設定するために、配列データを使用します。
<canvas id="canvas" width="300" height="227" style="float:left"></canvas> <div id="color" style="width:200px;height:50px;float:left"></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);
引数 dx
と dy
は、描画したいピクセルデータの左上の隅を描く位置を、コンテキストのデバイス座標で示します。
例えば myImageData
が表す画像全体をコンテキストの左上の隅から描くには、単純に以下のようにします:
ctx.putImageData(myImageData, 0, 0);
色のグレースケール化と反転
この例ではすべてのピクセルの値を変更するためにイテレートを行って、putImageData() を使用して変更後のピクセル配列を canvas に書き戻しています。invert 関数は、単純に最大値の 255 からそれぞれの色の値を減算します。grayscale 関数は、単純に赤、緑、青の平均値を使用します。また、例えば x = 0.299r + 0.587g + 0.114b
といった式による加重平均も使用できます。詳しくは Wikipedia の Grayscale (日本語版) をご覧ください。
<canvas id="canvas" width="300" height="227"></canvas> <div> <input id="grayscalebtn" value="グレースケール" type="button"> <input id="invertbtn" value="反転" type="button"> </div>
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
プロパティ (さまざまなブラウザ向けに接頭辞が必要です) の効果を確認できます。
ズームの例
<canvas id="canvas" width="300" height="227"></canvas> <canvas id="zoom" width="300" height="227"></canvas> <div> <label for="smoothbtn"> <input type="checkbox" name="smoothbtn" checked="checked" id="smoothbtn"> 画像のスムージングを有効にする </label> </div>
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
オブジェクトを作成します。