画像のカラーバリエーションをJavascriptで生成する

2013-04-23

カラーバリエーションの複数の画像が必要になるとき、1枚の画像から生成する方法を掲載します。

ホームページの装飾用のパーツなどで、カラーバリエーションが必要な場合は多いと思います。css3のfilterを使えば輝度とか彩度などを変更したりすることができますが、いかんせん対応しているブラウザが少ないため現状では利用がためらわれます。

今回は、地図上に配置する色違いのマーカ画像を1枚の画像から生成するために作りました。下のデモの画像は左端の赤いのがオリジナルで、それ以外は自動生成したカラーバリエーションです。

デモ

ちなみに上で表示してるマーカのサイズは32,64pxですが、iPhoneなどのRetinaに対応したGoogle Maps用のマーカです。

コード

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>画像のカラーバリエーションを生成</title>
<script>
(function(){
  function colors(){
    var colors = ['green', 'blue', 'cyan', 'yellow', 'violet', 'gray', 'black'];
    var img = new Image();
    img.onload = function(){
      for(var i=0;i<colors.length;i++) {
        var valiant = document.createElement('img');
        valiant.src = changeColor(img, colors[i]);
        document.getElementById('orgImg').parentNode.appendChild(valiant);
      }
    }
    img.src = document.getElementById('orgImg').src;
  }
  function changeColor(img, color){
    var iw = img.naturalWidth;
    var ih = img.naturalHeight;
    var canvas = document.createElement('canvas');
    canvas.width = iw;
    canvas.height = ih;
    var ctx = canvas.getContext('2d');
    ctx.save();
    ctx.drawImage(img, 0, 0);
    var pixels = ctx.getImageData(0, 0, iw, ih);
    var size = iw * ih;
    var func;
    switch(color){
      case 'green':
        func = function(p){
          var tmp = p[j];
          p[j] = p[j+1];
          p[j+1] = tmp;
        }
        break;
      case 'blue':
        func = function(p){
          var tmp = p[j];
          p[j] = p[j+2];
          p[j+2] = tmp;
        }
        break;
      case 'yellow':
        func = function(p){
          p[j+1] = p[j];
        }
        break;
      case 'violet':
        func = function(p){
          p[j+2] = p[j];
        }
        break;
      case 'cyan':
        func = function(p){
          var tmp = p[j];
          p[j] = p[j+2];
          p[j+1] = tmp;
          p[j+2] = tmp;
        }
        break;
      case 'gray':
        func = function(p){
          var gray = Math.floor(p[j]*.3 + p[j+1]*.59 + p[j+2]*.11);
          p[j] = p[j+1] = p[j+2] = gray;
        }
        break;
      case 'black':
      // 赤と緑の値に差がある(≒グレーでない)の画素だけ処理する
        func = function(p){
          if(Math.abs(p[j] - p[j+1]) >3) {
            var gray = Math.floor(p[j]*.3 + p[j+1]*.59 + p[j+2]*.11);
            p[j] = p[j+1] = p[j+2] = gray -50;
           }
        }
        break;
      default:
        break;
    }
    for(var j=0; j<size*4;j+=4){
      func(pixels.data);
    }
    ctx.putImageData(pixels, 0, 0);
    return ctx.canvas.toDataURL('image/png');
  }
  document.addEventListener('DOMContentLoaded', colors, false);
})();
</script>
</head>
<body>
<img id="orgImg" src="marker.png" />
</body>
</html>

やっていることは、単にピクセル毎に赤の画素を青にコピーしたり緑にコピーしたりしているだけなので、オリジナル画像は赤い画像である必要があります。あと事前にswitchで関数を定義しておりfor文の中で関数を何回も呼び出してますが、for文の中でswitchを繰り返し実行するのとどっちが効率がいいのか検討の余地はありそうです。

JavaScript