元画像を変更せずに画像をグレースケールで表示する

Category : CSS, JavaScript, SVG

タイトル通りなんですが、画像を1枚だけ用意して、それをカラーで使ったりグレースケールで使ったりしたいわけです。
IE には最初からフィルタが用意されていますが、クロスブラウザにできないかと思って調べてみました。

結果、何種類かあったので、デモを作ってみました。

元画像を変更せずに画像をグレースケールで表示する デモページ

IE 用の指定

filter: progid:DXImageTransform.Microsoft.BasicImage(grayscale=1);

グレースケールにしたいセレクタにこれを指定すればおっけー。

SVG フィルタを使う

Firefox 3.5 以上にしか使えないっぽいですが、SVG でフィルタを作って、それを CSS で指定します。
これは Stack Overflow にあった質問と回答からの引用です。

Your SVG file will look like this:

<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1"
     baseProfile="full"
     xmlns="http://www.w3.org/2000/svg">
    <filter id="desaturate">
        <feColorMatrix type="matrix" values="0.3333 0.3333 0.3333 0 0
                                             0.3333 0.3333 0.3333 0 0
                                             0.3333 0.3333 0.3333 0 0
                                             0      0      0      1 0"/>
    </filter>
</svg>

Save that as resources.svg, it can be reused from now on for any image you want to change to greyscale.
In your CSS you reference the filter using the Firefox specific filter property:

.target {
    filter: url(resources.svg#desaturate);
}

Firefox で HTML 内で使える SVG エフェクトについては以下のページを参照してください。

canvas を使ってグレースケールにして data URI に変換

これの解説は色んなところにありますが、一番分かりやすかったページを参考にしました。

IE 以外のモダンブラウザは HTML5 の canvas 要素をサポートしているので、それを使ってピクセルごとにグレースケールに変換する、という方法です。

All modern browsers support the CANVAS tag which is allowed access to the image’s pixels. The code below will go through each pixel in the image and replace the color values with an average value. Here is the code:

var canvas = document.createElement('canvas');

var canvasContext = canvas.getContext('2d');

var imgW = imgObj.width;

var imgH = imgObj.height;
canvas.width = imgW;
canvas.height = imgH;
canvasContext.drawImage(imgObj, 0, 0);

var imgPixels = canvasContext.getImageData(0, 0, imgW, imgH);

for(var y = 0; y < imgPixels.height; y++){
     for(var x = 0; x < imgPixels.width; x++){
          var i = (y * 4) * imgPixels.width + x * 4;
          var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3;
          imgPixels.data[i] = avg;
          imgPixels.data[i + 1] = avg;
          imgPixels.data[i + 2] = avg;
     }
}
canvasContext.putImageData(imgPixels, 0, 0, 0, 0, imgPixels.width, imgPixels.height);

return canvas.toDataURL();

From the above code sample, you see that we created a canvas, loaded the image into it, and then changed the color values of each pixel in the image to an average value. After that we simply replaced the SRC of our image to the image changed in the canvas.

Using the same method you can also flip images using JavaScript. (We will create a tutorial for this in the future).

この方法のいいところは、IE 以外のブラウザ(の最新版)なら、どれでも動くことです。
引用元ページでは jQuery を使ってブラウザを判別し、IE ならフィルタ、それ以外なら canvas という感じで分岐させていました。
※分岐しないと IE8 でエラー出ます。

ただし、ピクセルごとに変換するということは、画像が大きかったり多かったりすると、とても遅くなるということです。
画像をいっぱい用意してやってみると分かりやすいと思います。

調べた結果、上記の3種類になったわけですが、クロスブラウザにするには1番目と3番目を組み合わせるしかないっぽいです。
重いという点に目をつぶれば、ですが。

よほどの事情がない限りは最初からグレースケールにした画像を用意する方がよさそうですね。残念。

Leave a Reply