PHPで補色関係を色相環に描画

(1/1)
補色の組み合わせは互いの色を引き立て合う相乗効果がある。
今回は PHP を使い、あるカラーコードを指定すると、その補色を計算し、が色相環 (しきそうかん) の上に位置をプロットするプログラムを作ってみることにする。

サンプル・プログラムの実行例

色相環と補色

サンプル・プログラム

色相環と補色

色相環
虹の七色(赤・橙・黄・緑・青・藍・紫)をリング状に並べたものが色相環である。

色相環の 180 度反対側に位置する 2色を補色と呼ぶ。

補色の組み合わせは互いの色を引き立て合う相乗効果がありることから、補色調和と呼ぶ。

準備

0029: //TrueTypeフォント;環境に合わせて
0030: define('FONTFILE', '../../../../common/font/VL-PGothic-Regular.ttf');

今回、カラーコードをグラフィック上に文字として描画する関係で、TrueType フォントを用意しておく。詳しくは、「PHP で TrueType フォントを利用する」で紹介したとおりである。

解説:補色を計算する

0265: /**
0266:  * 補色を計算する
0267:  * @param string $hex #xxxxxx 形式のカラーコード
0268:  * @return string 補色のカラーコード/FALSE:入力値異常
0269: */
0270: function complementaryColor($hex) {
0271:     $rgb = array();
0272: 
0273:     if (hex2rgb($hex$rgb) == FALSE)   return FALSE;
0274: 
0275:     $rgb_max = max($rgb['r'], $rgb['g'], $rgb['b']);
0276:     $rgb_min = min($rgb['r'], $rgb['g'], $rgb['b']);
0277:     $cc = $rgb_min + $rgb_max;
0278:     $rgb['r'] = $cc - $rgb['r'];
0279:     $rgb['g'] = $cc - $rgb['g'];
0280:     $rgb['b'] = $cc - $rgb['b'];
0281: 
0282:     return rgb2hex($rgb);
0283: }

RGB で与えられたからコードから補色R'G'B'を求める計算式は次の通り――。

 mimetex 
 mimetex 
 mimetex 
 mimetex 

解説:RGB→HSL変換

0141: /**
0142:  * RGB→HSL変換
0143:  * @param int $r Red成分(0~255)
0144:  * @param int $g Green成分(0~255)
0145:  * @param int $b Blue成分(0~255)
0146:  * @param array $hsl 結果を格納する配列
0147:  *                  'h'=>色相(0~360度
0148:  *                  's'=>彩度(0~100%)
0149:  *                  'l'=>明度(0~100%)
0150:  * @return なし
0151: */
0152: function rgb2hsl($r$g$b, &$hsl) {
0153:     $rgb_max = max($r$g$b);
0154:     $rgb_min = min($r$g$b);
0155:     $hsl['h'] = 0;
0156:     $hsl['s'] = 0;
0157:     $hsl['l'] = ($rgb_max + $rgb_min) / 2;
0158: 
0159:     if ($rgb_max != $rgb_min) {
0160:         // H(色相)
0161:         if ($rgb_max == $r$hsl['h'] = 60 * ($g - $b) / ($rgb_max - $rgb_min);
0162:         if ($rgb_max == $g$hsl['h'] = 60 * ($b - $r) / ($rgb_max - $rgb_min) + 120;
0163:         if ($rgb_max == $b$hsl['h'] = 60 * ($r - $g) / ($rgb_max - $rgb_min) + 240;
0164:         // S(彩度)
0165:         if ($hsl['l'] <= 127) {
0166:             $hsl['s'] = ($rgb_max - $rgb_min) / ($rgb_max + $rgb_min);
0167:         } else {
0168:             $hsl['s'] = ($rgb_max - $rgb_min) / (510 - $rgb_max - $rgb_min);
0169:         }
0170:     }
0171:     if ($hsl['h'] < 0)   $hsl['h'] += 360;
0172: 
0173:     $hsl['h'] =  round($hsl['h']);
0174:     $hsl['s'] =  round($hsl['s'] * 100);
0175:     $hsl['l'] =  round(($hsl['l'] / 255) * 100);
0176: }

色相環は、文字通り、色相をリング状に並べたカラーチャートである。RGB色空間とは異なる、HLS色空間を使って描くことができる。

HSL色空間は、 H(色相;0~360 度)、S(彩度;0~100%)、L(明度;0~100%)の 3 値でカラーを表す。RGB から HSL への変換式は次の通りー。

 mimetex 
 mimetex 

 mimetex 

 mimetex 

 mimetex 

 mimetex 

解説:HSL→RGB変換

0178: /**
0179:  * HSL→RGB変換
0180:  * @param int $h 色相(0~360度)
0181:  * @param int $s 彩度(0~100%)
0182:  * @param int $l 明度(0~100%)
0183:  * @param array $rgb 結果を格納する配列
0184:  *                  'r'=>Red成分(0~255)
0185:  *                  'g'=>Green成分(0~255)
0186:  *                  'b'=>Blue成分(0~255)
0187: */
0188: function hsl2rgb($h$s$l, &$rgb) {
0189:     if ($h == 360)  $h = 0;
0190:  
0191:     if ($l <= 49) {
0192:         $hsl_max = 2.55 * ($l + $l * ($s / 100));
0193:         $hsl_min = 2.55 * ($l - $l * ($s / 100));
0194:     } else {
0195:         $hsl_max = 2.55 * ($l + (100 - $l) * ($s / 100));
0196:         $hsl_min = 2.55 * ($l - (100 - $l) * ($s / 100)); 
0197:     }
0198: 
0199:     if ($h < 60) {
0200:         $rgb['r'] = $hsl_max;
0201:         $rgb['g'] = $hsl_min + ($hsl_max - $hsl_min) * ($h / 60) ;
0202:         $rgb['b'] = $hsl_min;
0203:     } else if ($h >= 60 && $h < 120) {
0204:         $rgb['r'] = $hsl_min + ($hsl_max - $hsl_min) * ((120 - $h) / 60);
0205:         $rgb['g'] = $hsl_max;
0206:         $rgb['b'] = $hsl_min;
0207:     } else if ($h >= 120 && $h < 180) {
0208:         $rgb['r'] = $hsl_min;
0209:         $rgb['g'] = $hsl_max ;
0210:         $rgb['b'] = $hsl_min + ($hsl_max - $hsl_min) * (($h - 120) / 60);
0211:     } else if ($h >= 180 && $h < 240) {
0212:         $rgb['r'] = $hsl_min;
0213:         $rgb['g'] = $hsl_min + ($hsl_max - $hsl_min) * ((240 - $h) / 60);
0214:         $rgb['b'] = $hsl_max;
0215:     } else if ($h >= 240 && $h < 300) {
0216:         $rgb['r'] = $hsl_min + ($hsl_max - $hsl_min) * (($h - 240) / 60);
0217:         $rgb['g'] = $hsl_min;
0218:         $rgb['b'] = $hsl_max;
0219:     } else if ($h >= 300 && $h < 360) {
0220:         $rgb['r'] = $hsl_max;
0221:         $rgb['g'] = $hsl_min;
0222:         $rgb['b'] = $hsl_min + ($hsl_max - $hsl_min) * ((360 - $h) / 60);
0223:     }
0224: 
0225:     $rgb['r'] = round($rgb['r']);
0226:     $rgb['g'] = round($rgb['g']);
0227:     $rgb['b'] = round($rgb['b']);
0228: }

逆に、HSL から RGB への変換式は次の通りー。

 mimetex 

 mimetex 

 mimetex 

 mimetex 

 mimetex 

解説:色相環を描く

0285: /**
0286:  * 色相環を描画
0287:  * @param float  $r 半径
0288:  * @param array  $spots スポットする複数のカラーコード[省略可]
0289:  **                 #xxxxxx形式表記
0290:  * @return array(最小年,最大年)
0291: */
0292: function drawColorCircle($r$spots=NULL) {
0293:     $rgb = array();
0294:     $hsl = array();
0295: 
0296:     $canvas  = imagecreatetruecolor($r * 2, $r * 2);
0297:     $white = imagecolorallocate($canvas, 255, 255, 255);
0298:     $black = imagecolorallocate($canvas, 0, 0, 0);
0299:     imagecolortransparent($canvas$black);  //背景を透明に
0300: 
0301:     for ($h = 0; $h < 360; $h++) {
0302:         hsl2rgb($h, 100, 50, $rgb);
0303:         $color = imagecolorallocate($canvas$rgb['r'], $rgb['g'], $rgb['b']);
0304:         $start = $h - 90;
0305:         imagefilledarc($canvas$r$r$r * 2, $r * 2, $start$start + 2, $color, 0);
0306:     }
0307: 
0308:     //カラーコード(配列)をスポットする
0309:     if ($spots != NULL) {
0310:         foreach ($spots as $hex) {
0311:             if (hex2rgb($hex$rgb) == FALSE)   continue;
0312:             rgb2hsl($rgb['r'], $rgb['g'], $rgb['b'], $hsl);
0313:             $th = deg2rad($hsl['h'] - 90);
0314:             $x = ($r / 1.5) * cos($th);
0315:             $y = ($r / 1.5) * sin($th);
0316:             $c1 = imagecolorallocate($canvas$rgb['r'], $rgb['g'], $rgb['b']);
0317:             imagefilledellipse($canvas$r + $x$r + $y$r / 2, $r / 2, $c1);
0318:             imageellipse($canvas$r + $x$r + $y$r / 2, $r / 2, $white);
0319:             $hex = rgb2hex($rgb);
0320:             $cc = complementaryColor($hex);      //補色
0321:             hex2rgb($cc$rgb);
0322:             $c2 = imagecolorallocate($canvas$rgb['r'], $rgb['g'], $rgb['b']);
0323:             imagettftext($canvas$r / 16, 0, $r + $x - $r / 4 + $r / 16, $r + $y + $r / 32, $c2, FONTFILE, $hex);
0324:         }
0325:     }
0326: 
0327:     header('Content-type: image/png');       //MIMEはPNGで
0328:     imagepng($canvas);                       //ブラウザ表示
0329:     imagedestroy($canvas);                   //後処理
0330: }

色相環を描くのには GD関数を用いる。

まず、GD関数  imagecreatetruecolor  を使ってキャンパスを用意。続いて、GD関数  imagecolortransparent  を使って背景を透明にしておく。

色相環は、色相 $h を 0 度から 359 度まで回しながら、先ほど作ったユーザー関数 hsl2rgb によって RGB カラーを計算し、GD関数  imagefilledarc  を使って円弧を塗りつぶしていく。
なお、色相環は通常、90 度の位置を Red としていることから、色相 $h - 90 を角度として描画する。

次に、配列 $supots で受け取ったカラーコードにスポットを描画する。
先ほど作ったユーザー関数 rgb2hsl によって色相を算出し、これを角度として、先ほどの色相環の中のスポットする位置を三角関数によって求める。
そして、GD関数  imagefilledellipse  を使って、塗りつぶした円を描いてスポットにする。また、GD関数  imagettftext  を使ってカラーコードを文字として描画する。このとき、背景となるカラーコードから目立つように、文字カラーに補色を使った。

最期に、GD関数  imagepng  を使って画像を出力する。

参考サイト

(この項おわり)
header