Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
123 / 123 |
|
100.00% |
26 / 26 |
CRAP | |
100.00% |
1 / 1 |
Rgb | |
100.00% |
123 / 123 |
|
100.00% |
26 / 26 |
69 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
setR | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
setG | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
setB | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
setA | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
getR | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getG | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getB | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getA | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasA | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasAlpha | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
toCmyk | |
100.00% |
19 / 19 |
|
100.00% |
1 / 1 |
5 | |||
toGray | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
toHsl | |
100.00% |
21 / 21 |
|
100.00% |
1 / 1 |
9 | |||
toHex | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
toArray | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
4 | |||
render | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
7 | |||
__toString | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__set | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__get | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__isset | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__unset | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
offsetExists | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
4 | |||
offsetGet | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
6 | |||
offsetSet | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
6 | |||
offsetUnset | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (https://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@noladev.com> |
7 | * @copyright Copyright (c) 2009-2025 NOLA Interactive, LLC. |
8 | * @license https://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Color\Color; |
15 | |
16 | use OutOfRangeException; |
17 | |
18 | /** |
19 | * Pop Color RGB color class |
20 | * |
21 | * @category Pop |
22 | * @package Pop\Color |
23 | * @author Nick Sagona, III <dev@noladev.com> |
24 | * @copyright Copyright (c) 2009-2025 NOLA Interactive, LLC. |
25 | * @license https://www.popphp.org/license New BSD License |
26 | * @version 1.0.2 |
27 | */ |
28 | class Rgb extends AbstractColor implements \ArrayAccess |
29 | { |
30 | |
31 | /** |
32 | * R value |
33 | * @var int |
34 | */ |
35 | protected int $r = 0; |
36 | |
37 | /** |
38 | * G value |
39 | * @var int |
40 | */ |
41 | protected int $g = 0; |
42 | |
43 | /** |
44 | * B value |
45 | * @var int |
46 | */ |
47 | protected int $b = 0; |
48 | |
49 | /** |
50 | * Alpha value |
51 | * @var ?float |
52 | */ |
53 | protected ?float $a = null; |
54 | |
55 | /** |
56 | * Constructor |
57 | * |
58 | * Instantiate the CSS RGB color object |
59 | * |
60 | * @param int|string $r |
61 | * @param int|string $g |
62 | * @param int|string $b |
63 | * @param float|string|null $a |
64 | */ |
65 | public function __construct(int|string $r, int|string $g, int|string $b, float|string|null $a = null) |
66 | { |
67 | $this->setR($r); |
68 | $this->setG($g); |
69 | $this->setB($b); |
70 | if ($a !== null) { |
71 | $this->setA($a); |
72 | } |
73 | } |
74 | |
75 | /** |
76 | * Set R value |
77 | * |
78 | * @param int|string $r |
79 | * @throws OutOfRangeException |
80 | * @return self |
81 | */ |
82 | public function setR(int|string $r): self |
83 | { |
84 | $r = (int)$r; |
85 | if (($r > 255) || ($r < 0)) { |
86 | throw new OutOfRangeException('Error: The value of $r must be between 0 and 255.'); |
87 | } |
88 | $this->r = $r; |
89 | return $this; |
90 | } |
91 | |
92 | /** |
93 | * Set G value |
94 | * |
95 | * @param int|string $g |
96 | * @throws OutOfRangeException |
97 | * @return self |
98 | */ |
99 | public function setG(int|string $g): self |
100 | { |
101 | $g = (int)$g; |
102 | if (($g > 255) || ($g < 0)) { |
103 | throw new OutOfRangeException('Error: The value of $g must be between 0 and 255.'); |
104 | } |
105 | $this->g = $g; |
106 | return $this; |
107 | } |
108 | |
109 | /** |
110 | * Set B value |
111 | * |
112 | * @param int|string $b |
113 | * @throws OutOfRangeException |
114 | * @return self |
115 | */ |
116 | public function setB(int|string $b): self |
117 | { |
118 | $b = (int)$b; |
119 | if (($b > 255) || ($b < 0)) { |
120 | throw new OutOfRangeException('Error: The value of $b must be between 0 and 255.'); |
121 | } |
122 | $this->b = $b; |
123 | return $this; |
124 | } |
125 | |
126 | /** |
127 | * Set A value |
128 | * |
129 | * @param float|string $a |
130 | * @throws OutOfRangeException |
131 | * @return self |
132 | */ |
133 | public function setA(float|string $a): self |
134 | { |
135 | $a = (float)$a; |
136 | if (($a > 1) || ($a < 0)) { |
137 | throw new OutOfRangeException('Error: The value of $l must be between 0 and 1.'); |
138 | } |
139 | $this->a = $a; |
140 | return $this; |
141 | } |
142 | |
143 | /** |
144 | * Get R value |
145 | * |
146 | * @return int |
147 | */ |
148 | public function getR(): int |
149 | { |
150 | return $this->r; |
151 | } |
152 | |
153 | /** |
154 | * Get G value |
155 | * |
156 | * @return int |
157 | */ |
158 | public function getG(): int |
159 | { |
160 | return $this->g; |
161 | } |
162 | |
163 | /** |
164 | * Get B value |
165 | * |
166 | * @return int |
167 | */ |
168 | public function getB(): int |
169 | { |
170 | return $this->b; |
171 | } |
172 | |
173 | /** |
174 | * Get A value |
175 | * |
176 | * @return float|null |
177 | */ |
178 | public function getA(): float|null |
179 | { |
180 | return $this->a; |
181 | } |
182 | |
183 | /** |
184 | * Determine if the color object has an alpha value |
185 | * |
186 | * @return bool |
187 | */ |
188 | public function hasA(): bool |
189 | { |
190 | return ($this->a !== null); |
191 | } |
192 | |
193 | /** |
194 | * Determine if the color object has an alpha value (alias) |
195 | * |
196 | * @return bool |
197 | */ |
198 | public function hasAlpha(): bool |
199 | { |
200 | return ($this->a !== null); |
201 | } |
202 | |
203 | /** |
204 | * Convert to CMYK |
205 | * |
206 | * @return Cmyk |
207 | */ |
208 | public function toCmyk(): Cmyk |
209 | { |
210 | $K = 1; |
211 | |
212 | // Calculate CMY. |
213 | $cyan = 1 - ($this->r / 255); |
214 | $magenta = 1 - ($this->g / 255); |
215 | $yellow = 1 - ($this->b / 255); |
216 | |
217 | // Calculate K. |
218 | if ($cyan < $K) { |
219 | $K = $cyan; |
220 | } |
221 | if ($magenta < $K) { |
222 | $K = $magenta; |
223 | } |
224 | if ($yellow < $K) { |
225 | $K = $yellow; |
226 | } |
227 | |
228 | if ($K == 1) { |
229 | $cyan = 0; |
230 | $magenta = 0; |
231 | $yellow = 0; |
232 | } else { |
233 | $cyan = round((($cyan - $K) / (1 - $K)) * 100); |
234 | $magenta = round((($magenta - $K) / (1 - $K)) * 100); |
235 | $yellow = round((($yellow - $K) / (1 - $K)) * 100); |
236 | } |
237 | |
238 | $black = round($K * 100); |
239 | |
240 | return new Cmyk($cyan, $magenta, $yellow, $black); |
241 | } |
242 | |
243 | /** |
244 | * Convert to Gray |
245 | * |
246 | * @return Grayscale |
247 | */ |
248 | public function toGray(): Grayscale |
249 | { |
250 | return new Grayscale(floor(((floor(($this->r + $this->g + $this->b) / 3) / 255) * 100))); |
251 | } |
252 | |
253 | /** |
254 | * Convert to HSL |
255 | * |
256 | * @return Hsl |
257 | */ |
258 | public function toHsl(): Hsl |
259 | { |
260 | $r = $this->getR(); |
261 | $g = $this->getG(); |
262 | $b = $this->getB(); |
263 | |
264 | $min = min($r, min($g, $b)); |
265 | $max = max($r, max($g, $b)); |
266 | $delta = $max - $min; |
267 | $h = 0; |
268 | |
269 | if ($delta > 0) { |
270 | if ($max == $r && $max != $g) $h += ($g - $b) / $delta; |
271 | if ($max == $g && $max != $b) $h += (2 + ($b - $r) / $delta); |
272 | if ($max == $b && $max != $r) $h += (4 + ($r - $g) / $delta); |
273 | $h /= 6; |
274 | } |
275 | |
276 | // Calculate the saturation and brightness. |
277 | $r = $this->getR() / 255; |
278 | $g = $this->getG() / 255; |
279 | $b = $this->getB() / 255; |
280 | |
281 | $max = max($r, $g, $b); |
282 | $min = min($r, $g, $b); |
283 | |
284 | $l = $max; |
285 | $d = $max - $min; |
286 | $s = ($d == 0) ? 0 : $d / $max; |
287 | |
288 | return new Hsl(round($h * 360), round($s * 100), round($l * 100), $this->a); |
289 | } |
290 | |
291 | /** |
292 | * Convert to hex |
293 | * |
294 | * @return Hex |
295 | */ |
296 | public function toHex(): Hex |
297 | { |
298 | $hex = str_pad(dechex($this->r), 2, '0', STR_PAD_LEFT) . str_pad(dechex($this->g), 2, '0', STR_PAD_LEFT) . str_pad(dechex($this->b), 2, '0', STR_PAD_LEFT); |
299 | return new Hex($hex); |
300 | } |
301 | |
302 | /** |
303 | * Convert to array |
304 | * |
305 | * @param bool $assoc |
306 | * @return array |
307 | */ |
308 | public function toArray(bool $assoc = true): array |
309 | { |
310 | $rgb = []; |
311 | |
312 | if ($assoc) { |
313 | $rgb['r'] = $this->r; |
314 | $rgb['g'] = $this->g; |
315 | $rgb['b'] = $this->b; |
316 | if ($this->a !== null) { |
317 | $rgb['a'] = $this->a; |
318 | } |
319 | } else { |
320 | $rgb[] = $this->r; |
321 | $rgb[] = $this->g; |
322 | $rgb[] = $this->b; |
323 | if ($this->a !== null) { |
324 | $rgb[] = $this->a; |
325 | } |
326 | } |
327 | |
328 | return $rgb; |
329 | } |
330 | |
331 | /** |
332 | * Convert to readable string |
333 | * |
334 | * @param ?string $format |
335 | * @return string |
336 | */ |
337 | public function render(?string $format = null): string |
338 | { |
339 | if ($format == self::COMMA) { |
340 | return $this->r . ', ' . $this->g . ', ' . $this->b . (!empty($this->a) ? ', ' . $this->a : ''); |
341 | } else if ($format == self::CSS) { |
342 | return (($this->a !== null) ? 'rgba(' : 'rgb(') . implode(', ', $this->toArray()) . ')'; |
343 | } else if ($format == self::PERCENT) { |
344 | return round(($this->r / 255), 2) . ' ' . round(($this->g / 255), 2) . ' ' . round(($this->b / 255), 2); |
345 | } else { |
346 | return $this->r . ' ' . $this->g . ' ' . $this->b . (!empty($this->a) ? ' ' . $this->a : ''); |
347 | } |
348 | } |
349 | |
350 | /** |
351 | * Return CSS-formatted string |
352 | * |
353 | * @return string |
354 | */ |
355 | public function __toString(): string |
356 | { |
357 | return $this->render(self::CSS); |
358 | } |
359 | |
360 | /** |
361 | * Magic method to set the color value |
362 | * |
363 | * @param string $name |
364 | * @param mixed $value |
365 | * @throws Exception |
366 | * @return void |
367 | */ |
368 | public function __set(string $name, mixed $value): void |
369 | { |
370 | $this->offsetSet($name, $value); |
371 | } |
372 | |
373 | /** |
374 | * Magic method to return the color value |
375 | * |
376 | * @param string $name |
377 | * @throws Exception |
378 | * @return mixed |
379 | */ |
380 | public function __get(string $name): mixed |
381 | { |
382 | return $this->offsetGet($name); |
383 | } |
384 | |
385 | /** |
386 | * Magic method to return whether the color value exists |
387 | * |
388 | * @param string $name |
389 | * @return bool |
390 | */ |
391 | public function __isset(string $name): bool |
392 | { |
393 | return $this->offsetExists($name); |
394 | } |
395 | |
396 | /** |
397 | * Magic method to unset color value |
398 | * |
399 | * @param string $name |
400 | * @throws Exception |
401 | * @return void |
402 | */ |
403 | public function __unset(string $name): void |
404 | { |
405 | throw new Exception('You cannot unset the properties of this color object.'); |
406 | } |
407 | |
408 | /** |
409 | * ArrayAccess offsetExists |
410 | * |
411 | * @param mixed $offset |
412 | * @return bool |
413 | */ |
414 | public function offsetExists(mixed $offset): bool |
415 | { |
416 | return (($offset == 'r') || ($offset == 'g') || ($offset == 'b') || ($offset == 'a')); |
417 | } |
418 | |
419 | /** |
420 | * ArrayAccess offsetGet |
421 | * |
422 | * @param mixed $offset |
423 | * @throws Exception |
424 | * @return mixed |
425 | */ |
426 | public function offsetGet(mixed $offset): mixed |
427 | { |
428 | switch ($offset) { |
429 | case 'r': |
430 | return $this->getR(); |
431 | break; |
432 | case 'g': |
433 | return $this->getG(); |
434 | break; |
435 | case 'b': |
436 | return $this->getB(); |
437 | break; |
438 | case 'a': |
439 | return $this->getA(); |
440 | break; |
441 | default: |
442 | throw new Exception("Error: You can only use 'r', 'g', 'b' or 'a'."); |
443 | } |
444 | } |
445 | |
446 | /** |
447 | * ArrayAccess offsetSet |
448 | * |
449 | * @param mixed $offset |
450 | * @param mixed $value |
451 | * @throws Exception |
452 | * @return void |
453 | */ |
454 | public function offsetSet(mixed $offset, mixed $value): void |
455 | { |
456 | switch ($offset) { |
457 | case 'r': |
458 | $this->setR($value); |
459 | break; |
460 | case 'g': |
461 | $this->setG($value); |
462 | break; |
463 | case 'b': |
464 | $this->setB($value); |
465 | break; |
466 | case 'a': |
467 | $this->setA($value); |
468 | break; |
469 | default: |
470 | throw new Exception("Error: You can only use 'r', 'g', 'b' or 'a'."); |
471 | } |
472 | } |
473 | |
474 | /** |
475 | * ArrayAccess offsetUnset |
476 | * |
477 | * @param mixed $offset |
478 | * @throws Exception |
479 | * @return void |
480 | */ |
481 | public function offsetUnset(mixed $offset): void |
482 | { |
483 | throw new Exception('You cannot unset the properties of this color object.'); |
484 | } |
485 | |
486 | } |