Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
123 / 123
100.00% covered (success)
100.00%
26 / 26
CRAP
100.00% covered (success)
100.00%
1 / 1
Rgb
100.00% covered (success)
100.00%
123 / 123
100.00% covered (success)
100.00%
26 / 26
69
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 setR
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 setG
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 setB
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 setA
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 getR
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getG
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getB
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getA
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasA
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasAlpha
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toCmyk
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
5
 toGray
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toHsl
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
9
 toHex
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 toArray
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
4
 render
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
7
 __toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __set
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __get
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __isset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __unset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 offsetExists
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
4
 offsetGet
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
6
 offsetSet
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
6
 offsetUnset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
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 */
14namespace Pop\Color\Color;
15
16use 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 */
28class 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}