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

(1/1)
補色の組み合わせは互いの色を引き立て合う相乗効果がある。
今回はPHPを使い、あるカラーコードを指定すると、その補色を計算し、が色相環 (しきそうかん) の上に位置をプロットするプログラムを作ってみることにする。
(2022年4月9日)PHP8対応,リファラ・チェック改良

目次

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

色相環と補色

サンプル・プログラム

圧縮ファイルの内容
complementaryColor.phpサンプル・プログラム本体

色相環と補色

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

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

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

準備

0038: //表示幅(ピクセル)
0039: define('WIDTH', 600);
0040: 
0041: //色相環の半径
0042: define('RADIUS', 200);
0043: 
0044: //TrueTypeフォント;環境に合わせて
0045: define('FONTFILE', '../../../../common/font/VL-PGothic-Regular.ttf');

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

解説:補色を計算する

0300: /**
0301:  * 補色を計算する
0302:  * @param   string $hex #xxxxxx 形式のカラーコード
0303:  * @return  string 補色のカラーコード/FALSE:入力値異常
0304: */
0305: function complementaryColor($hex) {
0306:     $rgb = array();
0307: 
0308:     if (hex2rgb($hex$rgb) == FALSE)   return FALSE;
0309: 
0310:     $rgb_max = max($rgb['r'], $rgb['g'], $rgb['b']);
0311:     $rgb_min = min($rgb['r'], $rgb['g'], $rgb['b']);
0312:     $cc = $rgb_min + $rgb_max;
0313:     $rgb['r'] = $cc - $rgb['r'];
0314:     $rgb['g'] = $cc - $rgb['g'];
0315:     $rgb['b'] = $cc - $rgb['b'];
0316: 
0317:     return rgb2hex($rgb);
0318: }

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

 mimetex 
 mimetex 
 mimetex 
 mimetex 
 mimetex 
 mimetex 

解説:RGB→HSL変換

0176: /**
0177:  * RGB→HSL変換
0178:  * @param   int $r Red成分(0~255)
0179:  * @param   int $g Green成分(0~255)
0180:  * @param   int $b Blue成分(0~255)
0181:  * @param   array $hsl 結果を格納する配列
0182:  *                  'h'=>色相(0~360度
0183:  *                  's'=>彩度(0~100%)
0184:  *                  'l'=>明度(0~100%)
0185:  * @return  なし
0186: */
0187: function rgb2hsl($r$g$b, &$hsl) {
0188:     $rgb_max = max($r$g$b);
0189:     $rgb_min = min($r$g$b);
0190:     $hsl['h'] = 0;
0191:     $hsl['s'] = 0;
0192:     $hsl['l'] = ($rgb_max + $rgb_min) / 2;
0193: 
0194:     if ($rgb_max != $rgb_min) {
0195:         // H(色相)
0196:         if ($rgb_max == $r$hsl['h'] = 60 * ($g - $b) / ($rgb_max - $rgb_min);
0197:         if ($rgb_max == $g$hsl['h'] = 60 * ($b - $r) / ($rgb_max - $rgb_min) + 120;
0198:         if ($rgb_max == $b$hsl['h'] = 60 * ($r - $g) / ($rgb_max - $rgb_min) + 240;
0199:         // S(彩度)
0200:         if ($hsl['l'] <= 127) {
0201:             $hsl['s'] = ($rgb_max - $rgb_min) / ($rgb_max + $rgb_min);
0202:         } else {
0203:             $hsl['s'] = ($rgb_max - $rgb_min) / (510 - $rgb_max - $rgb_min);
0204:         }
0205:     }
0206:     if ($hsl['h'] < 0)  $hsl['h'] += 360;
0207: 
0208:     $hsl['h'] =  round($hsl['h']);
0209:     $hsl['s'] =  round($hsl['s'] * 100);
0210:     $hsl['l'] =  round(($hsl['l'] / 255) * 100);
0211: }

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

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

 mimetex 
 mimetex 

 mimetex 

 mimetex 

 mimetex 

 mimetex 

解説:HSL→RGB変換

0213: /**
0214:  * HSL→RGB変換
0215:  * @param   int $h 色相(0~360度)
0216:  * @param   int $s 彩度(0~100%)
0217:  * @param   int $l 明度(0~100%)
0218:  * @param   array $rgb 結果を格納する配列
0219:  *                  'r'=>Red成分(0~255)
0220:  *                  'g'=>Green成分(0~255)
0221:  *                  'b'=>Blue成分(0~255)
0222: */
0223: function hsl2rgb($h$s$l, &$rgb) {
0224:     if ($h == 360)  $h = 0;
0225:  
0226:     if ($l <= 49) {
0227:         $hsl_max = 2.55 * ($l + $l * ($s / 100));
0228:         $hsl_min = 2.55 * ($l - $l * ($s / 100));
0229:     } else {
0230:         $hsl_max = 2.55 * ($l + (100 - $l) * ($s / 100));
0231:         $hsl_min = 2.55 * ($l - (100 - $l) * ($s / 100)); 
0232:     }
0233: 
0234:     if ($h < 60) {
0235:         $rgb['r'] = $hsl_max;
0236:         $rgb['g'] = $hsl_min + ($hsl_max - $hsl_min) * ($h / 60) ;
0237:         $rgb['b'] = $hsl_min;
0238:     } else if ($h >= 60 && $h < 120) {
0239:         $rgb['r'] = $hsl_min + ($hsl_max - $hsl_min) * ((120 - $h) / 60);
0240:         $rgb['g'] = $hsl_max;
0241:         $rgb['b'] = $hsl_min;
0242:     } else if ($h >= 120 && $h < 180) {
0243:         $rgb['r'] = $hsl_min;
0244:         $rgb['g'] = $hsl_max ;
0245:         $rgb['b'] = $hsl_min + ($hsl_max - $hsl_min) * (($h - 120) / 60);
0246:     } else if ($h >= 180 && $h < 240) {
0247:         $rgb['r'] = $hsl_min;
0248:         $rgb['g'] = $hsl_min + ($hsl_max - $hsl_min) * ((240 - $h) / 60);
0249:         $rgb['b'] = $hsl_max;
0250:     } else if ($h >= 240 && $h < 300) {
0251:         $rgb['r'] = $hsl_min + ($hsl_max - $hsl_min) * (($h - 240) / 60);
0252:         $rgb['g'] = $hsl_min;
0253:         $rgb['b'] = $hsl_max;
0254:     } else if ($h >= 300 && $h < 360) {
0255:         $rgb['r'] = $hsl_max;
0256:         $rgb['g'] = $hsl_min;
0257:         $rgb['b'] = $hsl_min + ($hsl_max - $hsl_min) * ((360 - $h) / 60);
0258:     }
0259: 
0260:     $rgb['r'] = round($rgb['r']);
0261:     $rgb['g'] = round($rgb['g']);
0262:     $rgb['b'] = round($rgb['b']);
0263: }

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

 mimetex 

 mimetex 

 mimetex 

 mimetex 

 mimetex 

解説:色相環を描く

0320: /**
0321:  * 色相環を描画
0322:  * @param   float  $r 半径
0323:  * @param   array  $spots スポットする複数のカラーコード[省略可]
0324:  **                 #xxxxxx形式表記
0325:  * @return  array(最小年,最大年)
0326: */
0327: function drawColorCircle($r$spots=NULL) {
0328:     $rgb = array();
0329:     $hsl = array();
0330: 
0331:     $canvas  = imagecreatetruecolor($r * 2, $r * 2);
0332:     $white = imagecolorallocate($canvas, 255, 255, 255);
0333:     $black = imagecolorallocate($canvas, 0, 0, 0);
0334:     imagecolortransparent($canvas$black);  //背景を透明に
0335: 
0336:     for ($h = 0; $h < 360; $h++) {
0337:         hsl2rgb($h, 100, 50, $rgb);
0338:         $color = imagecolorallocate($canvas$rgb['r'], $rgb['g'], $rgb['b']);
0339:         $start = $h - 90;
0340:         imagefilledarc($canvas$r$r$r * 2, $r * 2, $start$start + 2, $color, 0);
0341:     }
0342: 
0343:     //カラーコード(配列)をスポットする
0344:     if ($spots != NULL) {
0345:         foreach ($spots as $hex) {
0346:             if (hex2rgb($hex$rgb) == FALSE)   continue;
0347:             rgb2hsl($rgb['r'], $rgb['g'], $rgb['b'], $hsl);
0348:             $th = deg2rad($hsl['h'] - 90);
0349:             $x = ($r / 1.5) * cos($th);
0350:             $y = ($r / 1.5) * sin($th);
0351:             $c1 = imagecolorallocate($canvas$rgb['r'], $rgb['g'], $rgb['b']);
0352:             imagefilledellipse($canvas$r + $x$r + $y$r / 2, $r / 2, $c1);
0353:             imageellipse($canvas$r + $x$r + $y$r / 2, $r / 2, $white);
0354:             $hex = rgb2hex($rgb);
0355:             $cc = complementaryColor($hex);      //補色
0356:             hex2rgb($cc$rgb);
0357:             $c2 = imagecolorallocate($canvas$rgb['r'], $rgb['g'], $rgb['b']);
0358:             imagettftext($canvas$r / 16, 0, $r + $x - $r / 4 + $r / 16, $r + $y + $r / 32, $c2FONTFILE$hex);
0359:         }
0360:     }
0361: 
0362:     header('Content-type: image/png');      //MIMEPNG
0363:     imagepng($canvas);                       //ブラウザ表示
0364:     imagedestroy($canvas);                   //後処理
0365: }

色相環を描くのには 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