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

サンプル・プログラム
complementaryColor.php | サンプル・プログラム本体 |
pahooInputData.php | データ入力に関わる関数群。 使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。 |
pahooColor.php | 色に関わるクラス pahooColor。 色に関わるクラスの使い方は「PHPでCSSカラー一覧を表示する」「PHPで補色関係を色相環に描画」を参照。include_path が通ったディレクトリに配置すること。 |
バージョン | 更新日 | 内容 |
---|---|---|
1.2.0 | 2024/12/21 | 主な変換式をクラス pahooColor に分離 |
1.1 | 2022/04/09 | PHP8対応,リファラ・チェック改良 |
1.0 | 2017/02/24 | 初版 |
バージョン | 更新日 | 内容 |
---|---|---|
1.8.0 | 2024/11/12 | validRegexPattern() 追加 |
1.7.0 | 2024/10/09 | validURL() validEmail() 追加 |
1.6.0 | 2024/10/07 | isButton() -- buttonタグに対応 |
1.5.0 | 2024/01/28 | exitIfExceedVersion() 追加 |
1.4.2 | 2024/01/28 | exitIfLessVersion() メッセージ修正 |
バージョン | 更新日 | 内容 |
---|---|---|
1.0.0 | 2024/12/21 | 初版 |
色相環と補色


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

補色の組み合わせは互いの色を引き立て合う相乗効果がありることから、補色調和と呼ぶ。
準備
complementaryColor.php
57: // 初期値(START) =============================================================
58: // 表示幅(ピクセル)
59: define('WIDTH', 600);
60:
61: // 色相環の半径
62: define('RADIUS', 200);
63:
64: // カラーコードの初期値
65: define('DEF_COLOR', '#FFCC00');
66:
67: // TrueTypeフォント;各自の環境に合わせて設定すること
68: define('FONTFILE', '../../../../common/font/VL-PGothic-Regular.ttf');
69:
70: // 色に関わるクラス:include_pathに配置すること
71: require_once('pahooColor.php');
72:
73: // 初期値(END) ===============================================================

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

テキスト入力やバージョンチェックは外部ファイル "pahooInputData.php" に分離しており、include_path が通ったディレクトリに配置すること。
色に関する処理はユーザー定義メソッド "pahooColor.php" に分離しており、こちらも include_path が通ったディレクトリに配置すること。
解説:補色を計算する
pahooColor.php
399: /**
400: * 補色を計算する
401: * @param string $hexcode = RGBコード(#ではじまる16進数6桁)
402: * @return string 補色のカラーコード/FALSE:入力値異常
403: * @参考URL https://www.pahoo.org/e-soul/webtech/phpgd/php06-gd-20.shtm
404: */
405: function complementaryColor($hex) {
406: $rgb = array();
407:
408: if (($rgb = $this->hex2rgb($hex)) == FALSE) return FALSE;
409:
410: $rgbMax = max($rgb['r'], $rgb['g'], $rgb['b']);
411: $rgbMin = min($rgb['r'], $rgb['g'], $rgb['b']);
412: $cc = $rgbMin + $rgbMax;
413: $rgb['r'] = $cc - $rgb['r'];
414: $rgb['g'] = $cc - $rgb['g'];
415: $rgb['b'] = $cc - $rgb['b'];
416:
417: return $this->rgb2hex($rgb);
418: }
\[
\displaystyle
\begin{eqnarray}
MAX &=& max(R_0, G_0, B_0)
\\\\
MIN &=& min(R_0, G_0, B_0)
\\\\
C &=& MAX + MIN
\\\\
R_1 &=& C - R_0
\\\\
G_1 &=& C - G_0
\\\\
B_1 &=& C - B_0
\end{eqnarray}
\]
解説:RGB→HSL変換
pahooColor.php
280: /**
281: * RGB成分を HSL成分に変換する
282: * @param array $rgb RGB成分
283: * 'r' => Red成分(0~255)
284: * 'g' => Green成分(0~255)
285: * 'b' => Blue成分(0~255)
286: * @return array HSL成分/FALSE:引数の異常
287: * 'h' => 色相(0~360度
288: * 's' => 彩度(0~100%)
289: * 'l' => 明度(0~100%)
290: * @参考URL https://www.pahoo.org/e-soul/webtech/phpgd/php06-gd-20.shtm
291: */
292: function rgb2hsl($rgb) {
293: // 引数のバリデーション
294: if (! $this->validateRGB($rgb)) {
295: $this->seterror(__FUNCTION__ . ' に渡したRGBのいずれかの値が異常です');
296: return FALSE;
297: }
298:
299: $rgbMax = max($rgb['r'], $rgb['g'], $rgb['b']);
300: $rgbMin = min($rgb['r'], $rgb['g'], $rgb['b']);
301:
302: $hsl['h'] = 0;
303: $hsl['s'] = 0;
304: $hsl['l'] = ($rgbMax + $rgbMin) / 2;
305:
306: if ($rgbMax != $rgbMin) {
307: // H(色相)
308: if ($rgbMax == $rgb['r']) {
309: $hsl['h'] = 60 * ($rgb['g'] - $rgb['b']) / ($rgbMax - $rgbMin);
310: }
311: if ($rgbMax == $rgb['g']) {
312: $hsl['h'] = 60 * ($rgb['b'] - $rgb['r']) / ($rgbMax - $rgbMin) + 120;
313: }
314: if ($rgbMax == $rgb['b']) {
315: $hsl['h'] = 60 * ($rgb['r'] - $rgb['g']) / ($rgbMax - $rgbMin) + 240;
316: }
317: // S(彩度)
318: if ($hsl['l'] <= 127) {
319: $hsl['s'] = ($rgbMax - $rgbMin) / ($rgbMax + $rgbMin);
320: } else {
321: $hsl['s'] = ($rgbMax - $rgbMin) / (510 - $rgbMax - $rgbMin);
322: }
323: }
324: if ($hsl['h'] < 0) {
325: $hsl['h'] += 360;
326: }
327:
328: $hsl['h'] = round($hsl['h']);
329: $hsl['s'] = round($hsl['s'] * 100);
330: $hsl['l'] = round(($hsl['l'] / 255) * 100);
331:
332: return $hsl;
333: }

HSL色空間は、 H(色相;0~360度)、S(彩度;0~100%)、L(明度;0~100%)の3値でカラーを表す。RGBからHSLへの変換式は次の通りー。
\[
\displaystyle
\begin{eqnarray}
MAX &=& max(r,g,b)
\\
\\
MIN &=& min(r,g,b)
\\
\\
H &=& \left\{
\begin{array}{ll}
undefined&:&if \quad MIN = MAX \\
\displaystyle
60\times\frac{G - R}{MAX - MIN}+ 60&:&if \quad MIN=B \\
\displaystyle
60\times\frac{B - G}{MAX - MIN}+ 60&:&if \quad MIN=R \\
\displaystyle
60\times\frac{R - B}{MAX - MIN}+ 60&:&if \quad MIN=G
\end{array}
\right.
\\
\\
l &=& \frac{MAX+MIN}{2}
\\
\\
S &=& \left\{
\begin{array}{ll}
\displaystyle
\frac{MAX-MIN}{MAX+MIN}&:&if \quad l\leq127 \\
\displaystyle
\frac{MAX-MIN}{510-MAX-MIN}&:&if \quad l\>127
\end{array}
\right.
\\
\\
\displaystyle
L &=& \frac{l}{255}\times100
\end{eqnarray}
\]
解説:HSL→RGB変換
pahooColor.php
335: /**
336: * HSL成分を RGB成分に変換する
337: * @param array $hsl HSL成分
338: * 'h' => 色相(0~360度
339: * 's' => 彩度(0~100%)
340: * 'l' => 明度(0~100%)
341: * @param array $rgb RGB成分
342: * 'r' => Red成分(0~255)
343: * 'g' => Green成分(0~255)
344: * 'b' => Blue成分(0~255)
345: * @参考URL https://www.pahoo.org/e-soul/webtech/phpgd/php06-gd-20.shtm
346: */
347: function hsl2rgb($hsl) {
348: // 引数のバリデーション
349: if (! $this->validateHSL($hsl)) {
350: $this->seterror(__FUNCTION__ . ' の引数が異常です');
351: return FALSE;
352: }
353:
354: if ($hsl['h'] == 360) {
355: $h = 0;
356: }
357:
358: if ($hsl['l'] <= 49) {
359: $hslMax = 2.55 * ($hsl['l'] + $hsl['l'] * ($hsl['s'] / 100));
360: $hslMin = 2.55 * ($hsl['l'] - $hsl['l'] * ($hsl['s'] / 100));
361: } else {
362: $hslMax = 2.55 * ($hsl['l'] + (100 - $hsl['l']) * ($hsl['s'] / 100));
363: $hslMin = 2.55 * ($hsl['l'] - (100 - $hsl['l']) * ($hsl['s'] / 100));
364: }
365:
366: if ($hsl['h'] < 60) {
367: $rgb['r'] = $hslMax;
368: $rgb['g'] = $hslMin + ($hslMax - $hslMin) * ($hsl['h'] / 60) ;
369: $rgb['b'] = $hslMin;
370: } else if ($hsl['h'] >= 60 && $hsl['h'] < 120) {
371: $rgb['r'] = $hslMin + ($hslMax - $hslMin) * ((120 - $hsl['h']) / 60);
372: $rgb['g'] = $hslMax;
373: $rgb['b'] = $hslMin;
374: } else if ($hsl['h'] >= 120 && $hsl['h'] < 180) {
375: $rgb['r'] = $hslMin;
376: $rgb['g'] = $hslMax ;
377: $rgb['b'] = $hslMin + ($hslMax - $hslMin) * (($hsl['h'] - 120) / 60);
378: } else if ($hsl['h'] >= 180 && $hsl['h'] < 240) {
379: $rgb['r'] = $hslMin;
380: $rgb['g'] = $hslMin + ($hslMax - $hslMin) * ((240 - $hsl['h']) / 60);
381: $rgb['b'] = $hslMax;
382: } else if ($hsl['h'] >= 240 && $hsl['h'] < 300) {
383: $rgb['r'] = $hslMin + ($hslMax - $hslMin) * (($hsl['h'] - 240) / 60);
384: $rgb['g'] = $hslMin;
385: $rgb['b'] = $hslMax;
386: } else if ($hsl['h'] >= 300 && $hsl['h'] < 360) {
387: $rgb['r'] = $hslMax;
388: $rgb['g'] = $hslMin;
389: $rgb['b'] = $hslMin + ($hslMax - $hslMin) * ((360 - $hsl['h']) / 60);
390: }
391:
392: $rgb['r'] = round($rgb['r']);
393: $rgb['g'] = round($rgb['g']);
394: $rgb['b'] = round($rgb['b']);
395:
396: return $rgb;
397: }

\[
\displaystyle
\begin{eqnarray}
MAX &=& \left\{
\begin{array}{ll}
\displaystyle
2.55 \times (L + L \times \frac{S}{100})&:&if \quad L\leq49 \\
\displaystyle
2.55 \times (L + (100 - L) \times \frac{S}{100})&:&if \quad L>49 \\
\end{array}
\right.
\\
\\
MIN &=& \left\{
\begin{array}{ll}
\displaystyle
2.55 \times (L - L \times \frac{S}{100})&:&if \quad L\leq49 \\
\displaystyle
2.55 \times (L - (100 - L) \times \frac{S}{100})&:&if \quad L>49 \\
\end{array}
\right.
\\
\\
\displaystyle
R &=& \left\{
\begin{array}{ll}
MAX&:&if \quad H<60\ or\ H\geq300 \\
\displaystyle
MIN + (MAX - MIN) \times \frac{120-H}{60}&:&if \quad H\geq60\ and\ H<120 \\
MIN&:&if \quad H\geq120\ and\ H<240 \\
\displaystyle
MIN + (MAX - MIN) \times \frac{H-240}{60}&:&if \quad H\geq240\ and\ H<300
\end{array}
\right.
\\
\\
\displaystyle
G &=& \left\{
\begin{array}{ll}
\displaystyle
MIN + (MAX - MIN) \times \frac{H}{60}&:&i \quad H<60 \\
MAX&:&if \quad H\geq60\ and\ H<180 \\
\displaystyle
MIN + (MAX - MIN) \times (\frac{240-H}{60})&:&if \quad H\geq180\ and\ H<240 \\
MIN&:&if\ H\geq240\ and\ H<360
\end{array}
\right.
\\
\\
\displaystyle
B &=& \left\{
\begin{array}{ll}
MIN&:&if \quad H<120 \\
\displaystyle
MIN + (MAX - MIN) \times \frac{H-120}{60}&:&if \quad H\geq120\ and\ H<180 \\
MAX&:&if \quad H\geq180\ and\ H<300 \\
\displaystyle
MIN + (MAX - MIN) \times \frac{360-H}{60}&:&if \quad H\geq300\ and\ H<360
\end{array}
\right.
\end{eqnarray}
\]
解説:色相環を描く
complementaryColor.php
136: /**
137: * 色相環を描画
138: * @param object $pcc pahooColorオブジェクト
139: * @param float $r 半径
140: * @param array $spots スポットする複数のカラーコード[省略可]
141: * #xxxxxx形式表記
142: * @return array(最小年,最大年)
143: */
144: function drawColorCircle($pcc, $r, $spots=NULL) {
145: $rgb = array();
146: $hsl = array();
147:
148: $canvas = imagecreatetruecolor($r * 2, $r * 2);
149: $white = imagecolorallocate($canvas, 255, 255, 255);
150: $black = imagecolorallocate($canvas, 0, 0, 0);
151: imagecolortransparent($canvas, $black); //背景を透明に
152:
153: for ($h = 0; $h < 360; $h++) {
154: $hsl['h'] = $h;
155: $hsl['s'] = 100;
156: $hsl['l'] = 50;
157: $rgb = $pcc->hsl2rgb($hsl);
158: $color = imagecolorallocate($canvas, $rgb['r'], $rgb['g'], $rgb['b']);
159: $start = $h - 90;
160: imagefilledarc($canvas, $r, $r, $r * 2, $r * 2, $start, $start + 2, $color, 0);
161: }
162:
163: //カラーコード(配列)をスポットする
164: if ($spots != NULL) {
165: foreach ($spots as $hex) {
166: if (($rgb = $pcc->hex2rgb($hex)) == FALSE) continue;
167: $hsl = $pcc->rgb2hsl($rgb);
168: $th = deg2rad($hsl['h'] - 90);
169: $x = ($r / 1.5) * cos($th);
170: $y = ($r / 1.5) * sin($th);
171: $c1 = imagecolorallocate($canvas, $rgb['r'], $rgb['g'], $rgb['b']);
172: imagefilledellipse($canvas, (int)($r + $x), (int)($r + $y), (int)($r / 2), (int)($r / 2), (int)$c1);
173: imageellipse($canvas, (int)($r + $x), (int)($r + $y), (int)($r / 2), (int)($r / 2), (int)$white);
174: $hex = $pcc->rgb2hex($rgb);
175: $cc = $pcc->complementaryColor($hex); //補色
176: $rgb = $pcc->hex2rgb($cc);
177: $c2 = imagecolorallocate($canvas, $rgb['r'], $rgb['g'], $rgb['b']);
178: imagettftext($canvas, (int)($r / 16), 0, (int)($r + $x - $r / 4 + $r / 16), (int)($r + $y + $r / 32), (int)$c2, FONTFILE, $hex);
179: }
180: }
181:
182: header('Content-type: image/png'); //MIMEはPNGで
183: imagepng($canvas); //ブラウザ表示
184: imagedestroy($canvas); //後処理
185: }

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

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

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

最期に、GD関数 imagepng を使って画像を出力する。
活用例
参考サイト
- 色相環:武蔵野美術大学
- PHP: GD および Image 関数:PHP公式
- パワーポイントの色の使い方 ~色相環編~:岡山のパソコン教室やましん ありがとう日記
- Illustratorで補色:Illustrator入門
- マンセルは、色の基本の基本ですよ!:インテリアコーディネーター合格への道
今回はPHPを使い、あるカラーコードを指定すると、その補色を計算し、色相環の上に位置をプロットするプログラムを作ってみることにする。