[Blog翻訳] Canvas のピクセルを直接操作する

みなさん、こんにちは。Windows 開発統括部の古内です。

今週、無事に Internet Explorer (IE) 9 の日本語版がリリースされました (こちらからダウンロード可能です)。そこで、IE9 に関する英語 Blog 記事をもう 1 つご紹介します。今回は、IE9 の目玉機能の 1 つである Canvas サポートの活用例について説明した Canvas Direct Pixel Manipulation という記事の翻訳です。2011 年 2 月 16 日に Developing for Windows Blog に投稿されたもので、IE9 RC 版を前提に書かれていますが、もちろん製品版でも有効です。


Canvas のピクセルを直接操作する

このブログでは、HTML5 の Canvas 要素と、ピクセルを直接操作する方法についてご紹介します。Internet Explorer 9 RC 版がリリースされたばかりなので、これらをご紹介する絶好のタイミングだと思います。Canvas のピクセルを直接操作すること自体は RC 版のリリース以前から可能だったのですが、そのときはまだパフォーマンスが不十分でした。では、さっそく操作してみましょう!

このデモを行うにあたって、簡易の画像エディターを作ってみました。このエディターでは、Canvas を使って描画された画像に対して、以下の操作を行うことができます。

  • 画像全体から、赤、緑、青のカラー チャネルを削除する
  • 選択した色を削除する
  • 特定の色をアルファに変換する (透明にする)
  • 画像全体をモノクロに変換する

下記がエディターの画面です (気が付いた方もいらっしゃると思いますが、Fish Tank (水槽) サンプルの背景画像を使いました)。

1

Canvas を使った画像の描画方法については、Windows Summit の「Canvas 入門 (英語)」 というセッション ビデオ (日本語注: このセッションのプレゼンテーション ファイルの日本語訳はこちらからダウンロード可能) の中で説明していますので、ここでは特に詳しく触れず、その先の画像の生データの取得のところから説明することにします。

まず、Canvas から ImageData オブジェクトを取得します。

imgData = ctx.getImageData(0,0,WIDTH, HEIGHT);

ImageData オブジェクトの CanvasPixelArray フィールドは、画像の実際の生データをピクセル形式で表しています。

var pixels = imgData.data;

次に、上記メソッドから返されるピクセル配列の構成について説明します。各ピクセルは、4 バイトのデータで表現されます。

  • 1 バイト目は、赤のチャネル (R) 
  • 2 バイト目は、緑のチャネル (G)
  • 3 バイト目は、青のチャネル (B)
  • 4 バイト目は、アルファ チャネル (A)

それぞれの値は、0 ~ 255 までの整数です。各ピクセルは左から右、上から下へと処理され、インデックスは 0 から始まります。たとえば下の図ように、画像の幅がインデックス 6 のピクセルまである場合、最上段、最左列のピクセルの赤コンポーネントは、配列上ではインデックス 0 です。2 行目、2 列目のピクセルの赤コンポーネントは、以下の位置 (インデックス) に格納されています。

6 * 4 + 2 * 4 = 32

2

上記の点を踏まえれば、汎用的な計算式を作成して、あらゆるピクセル位置にあるカラー コンポーネントを取得することができます。

Red = pixels [(row * 4 * width) + (column * 4)];

Green = pixels [(row * 4 * width) + (column * 4) + 1];

Blue = pixels [(row * 4 * width) + (column * 4) + 2];

Alpha = pixels [(row * 4 * width) + (column * 4) + 3];

列は Canvas の X 座標に一致し、行は Canvas の Y 座標に一致します。そこで、JavaScript で以下のように記述すれば、座標 (tmpxX、tmpY) を使って画像の全ピクセルの色を取得できます。

var colorOffset = {red: 0, green: 1, blue: 2, alpha: 3};

rNew = imgData.data [ (4 * tmpY * WIDTH) + (4 * tmpX) + colorOffset.red ];

gNew = imgData.data [ (4 * tmpY * WIDTH) + (4 * tmpX) + colorOffset.green];

bNew = imgData.data [ (4 * tmpY * WIDTH) + (4 * tmpX) + colorOffset.blue];

aNew = imgData.data [ (4 * tmpY * WIDTH) + (4 * tmpX) + colorOffset.alpha];

色を操作するには、各ピクセルに対する繰り返し処理の中で、該当するカラー コンポーネントを必要に応じて変更します。以下がそのためのループ処理です。

for (var i = 0; i < pixels.length; i += 4) {

}

このループ処理を使えば、あらゆる操作を行うことができます。この簡易画像エディターでは以下のような処理を行っています。もちろん皆さんも、これを応用してお好きなように色を操作することができます。

1. 特定のチャネルを削除するには、該当するカラー チャネルに 0 をセットします。たとえば緑を削除する場合は以下のようにします。

pixels[i + colorOffset.green] = 0;

2. 特定の色を削除するには、各カラー チャネルからその色に対応する値を引きます。

pixels [i + colorOffset.red] -= rNew;

pixels [i + colorOffset.green] -= gNew;

pixels [i + colorOffset.blue] -= bNew;

3. 画像全体をモノクロに変換するには、赤、緑、青のチャネルに輝度値をセットします。輝度値を求めるにはこちらのページ (英語) にある式を使います。

var brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b;

pixels[i + colorOffset.red] = brightness;

pixels[i + colorOffset.green] = brightness;

pixels[i + colorOffset.blue] = brightness;

4. 特定の色をアルファに変換する (つまり、透明にする、あるいは色を削除する) には、2 段階で処理します。まず、処理対象のピクセルの色が選択された色に近いかどうかをチェックします。近い色なら、そのピクセルのアルファ チャネルに 0 をセットします。

if ( Math.abs (r-rNew) < 10 &&

Math.abs (g-gNew) < 10 &&

Math.abs (b-bNew) < 10

)

pixels[i + colorOffset.alpha] = 0;

みなさんなら、この方法を使ってどんなことをしますか?

ご説明したとおり、生のピクセル データに直接アクセスして操作すれば、さまざまなカラー エフェクトを施したり、赤目を除去したりと、ありとあらゆることができるようになります。しかしこれだけではありません。ここで紹介した方法を応用し、映像を操作することもできます。次回の私のブログではそれについて取り上げます。

ご一読ありがとうございました。お役に立てれば幸いです。

ソース コードはこちらからダウンロードできます。