RGB値からHTMLカラーコードに変換する

RGB値(255,255,255 など)からHTMLカラーコード(#FFFFFFなど)に変換する。
ついでにできるだけ最速の方法を模索してみたいと思います。

カラーコードは6桁の16進数です。
Red,Green,Blueの全ての値が255の場合、それらの値を色で表すと

#FFFFFF
となります。

これを2進数で表すと
111111111111111111111111
となります。

つまり、10進数の255は16進数では"FF"、2進数では"11111111"となります。

Red,Green,Blueは最大255、つまり8ビットで表されることが理解できたところで以下の結果を見て下さい。
var digit = 255;
alert(digit.toString(16)); //FF
alert(digit.toString(2)); //11111111

var digit = 255 << 8;
alert(digit); //65280
alert(digit.toString(16)); //FF00
alert(digit.toString(2)); //1111111100000000

var digit = 65280 << 8;
alert(digit); //16711680
alert(digit.toString(16)); //FF0000
alert(digit.toString(2)); //111111110000000000000000
以上のことから
var
red = 255,
green = 255,
blue = 255;

alert(((red << 16) + (green << 8) + blue).toString(16));
//FFFFFF
となることが理解できると思います。

つまり、
FFFFFF == FF0000 + FF00 + FF
となるような計算を行いたいわけですね。


ビットシフトを使って処理する

上の結果から想像できると思いますが、Greenの値は8桁ビットシフトします。Redの値は16桁ビットシフトします。そして全ての値を足した後、16進数に変換します。

var x = 255;
alert((x).toString(2)); //11111111
alert((x << 8).toString(2)); //1111111100000000
alert((x << 16).toString(2)); //111111110000000000000000

alert((16777216).toString(16)); //1000000

上記を踏まえた処理が以下になります。
function toColorCode(r, g, b){
    return '#' + (((r << 8) + g << 8) + b).toString(16);
}

alert(toColorCode(0,0,128)); //#80
これでは上の結果ように桁が合わない場合が出てしまいます。

この問題を解消するために2進数で表すところの『1000000000000000000000000』、10進数で『16777216』、16進数で『1000000』を足すことによって一時的に桁を合わせ、最後に先頭の"1"を取り除く、という処理を行います。
//713ms
function toColorCode_1a(r,g,b){
  return (((256 + r << 8) + g << 8) + b).toString(16).replace(/^1/,'#');
}

//463ms
function toColorCode_1b(r,g,b){
  return '#' + (((256 + r << 8) + g << 8) + b).toString(16).substr(1, 6);
}
上はreplaceを使い先頭の"1"を"#"に置き換えています。下はsubstrで"1"より右側の文字列を取得しています。

コメントの計測タイムは以下の処理のとおり、loopで回して処理に掛った時間です。
var x = '';
console.time('test');
for (var i = 0; i < 1000 * 1000; i++) {
  x = toColorCode(255, 255, 255);
}
console.timeEnd('test');
console.log(x);



素直に処理してみる

ビットシフトを使わず、素直に数値を使って処理してみます。
考え方は上と同じです。
256を掛けてやるとビットシフトと同じ効果になります。
//713ms
function toColorCode_2a(r, g, b){
  return (((256 + r) * 256 + g) * 256 + b).toString(16).replace(/^1/, '#');
}

//478ms
function toColorCode_2b(r, g, b){
  return '#' + (((256 + r) * 256 + g) * 256 + b).toString(16).substr(1, 6);
}
以外にもビットシフトを使った方が処理が速いですね。



他の方法を試す

無駄と分かっていても一応いろいろと試してみたくなる衝動にまかせて書いてみる。
//firefox3.5
//14443ms
function toColorCode_3a(r, g, b){
  return '#' + [r, g, b].map(function(cp){
    return (cp > 16 ? '' : '0') + cp.toString(16);
  }).join('');
}

//use jQuery
//5380ms
function toColorCode_3b(r, g, b){
  return '#' + $.map([r, g, b], function(cp){
    return (cp > 16 ? '' : '0') + cp.toString(16);
  }).join('');
}

//2250ms
function toColorCode_3c(r, g, b){
  return (function(rgb){
    var ret = '#';
    for (var i = 0, len = rgb.length; i < len; i++)
      ret += (rgb[i] > 16 ? '' : '0') + rgb[i].toString(16);

    return ret;
  })([r, g, b]);
}

//623ms
function toColorCode_3d(r, g, b){
  return '#' +
    (r > 16 ? '' : '0') + r.toString(16) +
    (g > 16 ? '' : '0') + g.toString(16) +
    (b > 16 ? '' : '0') + b.toString(16);
}
どれも上の処理に比べると恐ろしく遅い。



最速はsliceを使った以下になりましたとさ。
function toColorCode(r,g,b){
  return '#' + (((256 + r << 8) + g << 8) + b).toString(16).slice(1);
}

0 Comments:

Recent Posts