JavaScript / PHP：RGB HSV HSL 相互変換

## hsv2rgb ##
HSVからRGBに変換します。

hsv2rgb単独で動作します。

引数は h, s, v の順で渡します。
h = 0以上360未満の浮動小数点数
s = 0～1の浮動小数点数
v = 0～1の浮動小数点数

戻り値はRGBに変換した各値を連想配列で返します。
hex : #RRGGBB 形式の文字列
rgb : R, G, B 各値(0～255)の配列
r: Rの値(0～255の整数)
g: Gの値(0～255の整数)
b: Bの値(0～255の整数)

## rgb2hsv ##
RGBからHSVに変換します。

rgb2hsv単独で動作します。

引数は r, g, b の順、または色コードで渡します。
r = 0～255の整数
g = 0～255の整数
b = 0～255の整数

色コードの場合は16進数6桁、または3桁で渡せます。
例えば3桁で18fと渡した場合は1188ffとして扱います。
11ffや221188ffなど6桁でも3桁でもない場合は、0011ffや1188ff等として扱います。
先頭に'#'は付けても付けなくても構いません。

戻り値はHSVに変換した各値を連想配列で返します。
h: Hの値(0以上360未満の浮動小数点数)
s: Sの値(0～1の浮動小数点数)
v: Vの値(0～1の浮動小数点数)


## hsl2rgb ##
HSLからRGBに変換します。

hsl2rgbの他、sl2sv、hsv2rgbが必要です。

引数は h, s, l の順で渡します。
h = 0以上360未満の浮動小数点数
s = 0～1の浮動小数点数
l = 0～1の浮動小数点数

戻り値はhsv2rgbと同じです。

## hsl2hsv ##
HSLからHSVに変換します。

hsl2hsvの他、sl2svが必要です。

引数はhsl2rgbと同じです。

戻り値はrgb2hsvと同じです。

## rgb2hsl ##
RGBからHSLに変換します。

rgb2hslの他、rgb2hsv、sv2slが必要です。

引数はrgb2hsvと同じです。

戻り値はHSLに変換した各値を連想配列で返します。
h: Hの値(0以上360未満の浮動小数点数)
s: Sの値(0～1の浮動小数点数)
l: Lの値(0～1の浮動小数点数)

## hsv2hsl ##
HSVからHSLに変換します。

hsv2hslの他、sv2slが必要です。

引数はhsv2rgbと同じです。

戻り値はrgb2hslと同じです。

----

## スクリプト ##

### JavaScript版 ###

```js:color_convert.js
/**
 *  hsv2rgb (h, s, v)
 */
function hsv2rgb(h, s, v) {
  // 引数処理
  h = (h < 0 ? h % 360 + 360 : h) % 360 / 60;
  s = s < 0 ? 0 : s > 1 ? 1 : s;
  v = v < 0 ? 0 : v > 1 ? 1 : v;

  // HSV to RGB 変換
  const c = [5, 3, 1].map(n =>
    Math.round((v - Math.max(0, Math.min(1, 2 - Math.abs(2 - (h + n) % 6))) * s * v) * 255));

  // 戻り値
  return {
    hex: `#${(c[0] << 16 | c[1] << 8 | c[2]).toString(16).padStart(6, '0')}`,
    rgb: c, r: c[0], g: c[1], b: c[2],
  };
}

/**
 *  rgb2hsv (r, g, b)
 *  rgb2hsv (colorcode)
 */
function rgb2hsv(r, g, b) {
  // 引数処理
  let tmp = [r, g, b];
  if (r !== void 0 && g === void 0) {
    const cc = parseInt(r.toString().replace(/[^\da-f]/ig, '').replace(/^(.)(.)(.)$/, "$1$1$2$2$3$3"), 16);
    tmp = [cc >> 16 & 0xff, cc >> 8 & 0xff, cc & 0xff];
  }
  else {
    for (let i in tmp) tmp[i] = Math.max(0, Math.min(255, Math.floor(tmp[i])));
  }
  [r, g, b] = tmp;

  // RGB to HSV 変換
  const
    v = Math.max(r, g, b), d = v - Math.min(r, g, b),
    s = v ? d / v : 0, a = [r, g, b, r, g], i = a.indexOf(v),
    h = s ? (((a[i + 1] - a[i + 2]) / d + i * 2 + 6) % 6) * 60 : 0;

  // 戻り値
  return {h, s, v: v / 255};
}

function sl2sv(s, l) {
  s = s < 0 ? 0 : s > 1 ? 1 : +s;
  l = l < 0 ? 0 : l > 1 ? 1 : +l;
  const
    n = l + s * Math.min(l, 1 - l),
    s_ = n ? (1 - l / n) * 2 : sl2sv(s, .5)[0];
  return [s_, n];
}

function sv2sl(s, v) {
  s = s < 0 ? 0 : s > 1 ? 1 : +s;
  v = v < 0 ? 0 : v > 1 ? 1 : +v;
  const
    n = v * (1 - s / 2),
    s_ = n ? (v - n) / Math.min(n, 1 - n) : sv2sl(s, .5)[0];
  return [s_, n];
}

/**
 *  hsl2rgb (h, s, l)
 */
function hsl2rgb(h, s, l) {
  const sv = sl2sv(s, l);
  return hsv2rgb(h, sv[0], sv[1]);
}

/**
 *  hsl2hsv (h, s, l)
 */
function hsl2hsv(h, s, l) {
  const sv = sl2sv(s, l);
  return {h, s: sv[0], v: sv[1]};
}

/**
 *  rgb2hsl (r, g, b)
 *  rgb2hsl (colorcode)
 */
function rgb2hsl(r, g, b) {
  const
    hsv = rgb2hsv(r, g, b),
    sl = sv2sl(hsv.s, hsv.v);
  return {h: hsv.h, s: sl[0], l: sl[1]};
}

/**
 *  hsv2hsl (h, s, v)
 */
function hsv2hsl(h, s, v) {
  const sl = sv2sl(s, v);
  return {h, s: sl[0], l: sl[1]};
}
```

#### example ####

```js:
const colorCode = '#4f8eac';
const hsv = rgb2hsv(colorCode);
console.log(`rgb2hsv('${colorCode}')`);
console.log(hsv);
console.log('h = ' + hsv.h);
console.log('s = ' + hsv.s);
console.log('v = ' + hsv.v);
/*
  rgb2hsv('#4f8eac')
  { h: 199.3548387096774, s: 0.5406976744186046, v: 0.6745098039215687 }
  h = 199.3548387096774
  s = 0.5406976744186046
  v = 0.6745098039215687
*/

const rgb = hsv2rgb(hsv.h, hsv.s, hsv.v);
console.log(`hsv2rgb(${hsv.h}, ${hsv.s}, ${hsv.v})`);
console.log(rgb);
console.log(rgb.rgb);
console.log('hex = ' + rgb.hex);
console.log('r = ' + rgb.r);
console.log('g = ' + rgb.g);
console.log('b = ' + rgb.b);
/*
  hsv2rgb(199.3548387096774, 0.5406976744186046, 0.6745098039215687)
  { hex: '#4f8eac', rgb: [ 79, 142, 172 ], r: 79, g: 142, b: 172 }
  [ 79, 142, 172 ]
  hex = #4f8eac
  r = 79
  g = 142
  b = 172
*/

console.log(rgb2hsl(255, 128, 64));
  // { h: 20.104712041884838, s: 1, l: 0.6254901960784314 }
console.log(hsl2rgb(20.104712041884838, 1, 0.6254901960784314));
  // { hex: '#ff8040', rgb: [ 255, 128, 64 ], r: 255, g: 128, b: 64 }
console.log(hsv2hsl(123, 0.75, 0.5));
  // { h: 123, s: 0.6, l: 0.3125 }
console.log(hsl2hsv(123, 0.6, 0.3125));
  // { h: 123, s: 0.75, v: 0.5 }
```

### [JavaScript版を使用した相互変換デモ](https://akebi.jp/temp/color_convert.html) ###

----

### PHP版 ###

```php:color_convert.php
<?php
/**
 *  hsv2rgb (h, s, v)
 */
function hsv2rgb($h, $s, $v) {
  // 引数処理
  $h = ($h < 0 ? 360 + fmod($h, 360) : fmod($h, 360)) / 60;
  $s = max(0, min(1, $s));
  $v = max(0, min(1, $v));

  // HSV to RGB 変換
  foreach([5, 3, 1] as $k => $val) {
    $c[$k] = round(($v - max(0, min(1, 2 - abs(2 - fmod($h + $val, 6)))) * $s * $v) * 255);
  }

  // 戻り値
  return [
    'hex' => sprintf('#%02x%02x%02x', $c[0], $c[1], $c[2]),
    'rgb' => $c,
    'r' => $c[0],
    'g' => $c[1],
    'b' => $c[2],
  ];
}

/**
 *  rgb2hsv (r, g, b)
 *  rgb2hsv (colorcode)
 */
function rgb2hsv($r, $g = null, $b = null) {
  // 引数処理
  $tmp = [$r, $g, $b];
  if($r !== null && $g === null) {
    $cc = hexdec(preg_replace('/^(.)(.)(.)$/', "$1$1$2$2$3$3", preg_replace('/[^\da-f]/i', '' , $r)));
    $tmp = [$cc >> 16 & 0xff, $cc >> 8 & 0xff, $cc & 0xff];
  }
  else {
    foreach($tmp as $k => $v) $tmp[$k] = max(0, min(255, (int)$v));
  }
  [$r, $g, $b] = $tmp;

  // RGB to HSV 変換
  $v = max($r, $g, $b);
  $d = $v - min($r, $g, $b);
  $s = $v ? $d / $v : 0;
  $i = array_search($v, $a = [$r, $g, $b, $r, $g]);
  $h = $s ? fmod(($a[$i + 1] - $a[$i + 2]) / $d + $i * 2 + 6, 6) * 60 : 0;

  // 戻り値
  return [
    'h' => $h,
    's' => $s,
    'v' => $v / 255,
  ];
}

function sl2sv($s, $l) {
  $s = max(0, min(1, $s));
  $l = max(0, min(1, $l));
  $n = $l + $s * min($l, 1 - $l);
  $s_ = $n ? (1 - $l / $n) * 2 : sl2sv($s, .5)[0];
  return [$s_, $n];
}

function sv2sl($s, $v) {
  $s = max(0, min(1, $s));
  $v = max(0, min(1, $v));
  $n = $v * (1 - $s / 2);
  $s_ = $n ? ($v - $n) / min($n, 1 - $n) : sv2sl($s, .5)[0];
  return [$s_, $n];
}

/**
 *  hsl2rgb (h, s, l)
 */
function hsl2rgb($h, $s, $l) {
  $sv = sl2sv($s, $l);
  return hsv2rgb($h, $sv[0], $sv[1]);
}

/**
 *  hsl2hsv (h, s, l)
 */
function hsl2hsv($h, $s, $l) {
  $sv = sl2sv($s, $l);
  return ['h' => $h, 's' => $sv[0], 'v' => $sv[1]];
}

/**
 *  rgb2hsl (r, g, b)
 *  rgb2hsl (colorcode)
 */
function rgb2hsl($r, $g, $b) {
  $hsv = rgb2hsv($r, $g, $b);
  $sl = sv2sl($hsv['s'], $hsv['v']);
  return ['h' => $hsv['h'], 's' => $sl[0], 'l' => $sl[1]];
}

/**
 *  hsv2hsl (h, s, v)
 */
function hsv2hsl($h, $s, $v) {
  $sl = sv2sl($s, $v);
  return ['h' => $h, 's' => $sl[0], 'l' => $sl[1]];
}
```

#### example ####

```php:
$colorCode = '#4f8eac';

print "rgb2hsv('{$colorCode}')\n";
$hsv = rgb2hsv($colorCode);
print_r($hsv);
/*
  rgb2hsv('#4f8eac')
  Array
  (
      [h] => 199.35483870968
      [s] => 0.5406976744186
      [v] => 0.67450980392157
  )
*/

print "hsv2rgb({$hsv['h']}, {$hsv['s']}, {$hsv['v']})\n";
$rgb = hsv2rgb($hsv['h'], $hsv['s'], $hsv['v']);
print_r($rgb);
/*
  hsv2rgb(199.35483870968, 0.5406976744186, 0.67450980392157)
  Array
  (
      [hex] => #4f8eac
      [rgb] => Array
          (
              [0] => 79
              [1] => 142
              [2] => 172
          )

      [r] => 79
      [g] => 142
      [b] => 172
  )
*/

print_r(rgb2hsl(255, 128, 64));
/*
  Array
  (
      [h] => 20.104712041885
      [s] => 1
      [l] => 0.62549019607843
  )
*/

print_r(hsl2rgb(20.104712041885, 1, 0.62549019607843));
/*
  Array
  (
      [hex] => #ff8040
      [rgb] => Array
          (
              [0] => 255
              [1] => 128
              [2] => 64
          )

      [r] => 255
      [g] => 128
      [b] => 64
  )
*/

print_r(hsv2hsl(123, 0.75, 0.5));
/*
  Array
  (
      [h] => 123
      [s] => 0.6
      [l] => 0.3125
  )
*/

print_r(hsl2hsv(123, 0.6, 0.3125));
/*
  Array
  (
      [h] => 123
      [s] => 0.75
      [v] => 0.5
  )
*/
```

----
**関連記事 [JavaScript input要素に対して使用可能なカラーピッカーUIを作成](https://qiita.com/akebi_mh/items/3fba059989b460eb997d)**