diff --git a/src/Color.php b/src/Color.php index c1e5d15..907026d 100644 --- a/src/Color.php +++ b/src/Color.php @@ -83,6 +83,40 @@ public static function RGBToHSL(int $red, int $green, int $blue): array return array(round($hue), round($saturation, 2), round($lightness, 2)); } + /** + * Calculate the components Chroma, Value, and Hue based on RGB color. + * + * @param int $red The red component of the RGB color (0-255). + * @param int $green The green component of the RGB color (0-255). + * @param int $blue The blue component of the RGB color (0-255). + * @return array An array containing the calculated values (maxRGB, minRGB, chroma, value, hue). + */ + public static function calculateCVH(int $red, int $green, int $blue): array + { + $normalizedRed = $red / 255; + $normalizedGreen = $green / 255; + $normalizedBlue = $blue / 255; + + $maxRGB = max($normalizedRed, $normalizedGreen, $normalizedBlue); + $minRGB = min($normalizedRed, $normalizedGreen, $normalizedBlue); + $chroma = $maxRGB - $minRGB; + $value = $maxRGB; // also called brightness + if ($chroma == 0) { + $hue = 0; + } elseif ($maxRGB == $normalizedRed) { + $hue = 60 * (($normalizedGreen - $normalizedBlue) / $chroma); + } elseif ($maxRGB == $normalizedGreen) { + $hue = 60 * (2 + ($normalizedBlue - $normalizedRed) / $chroma); + } else { + $hue = 60 * (4 + ($normalizedRed - $normalizedGreen) / $chroma); + } + + if ($hue < 0) { + $hue += 360; + } + return array($maxRGB, $minRGB, $chroma, $value, $hue); + } + /** * Convert RGB color to HSV color space. * @@ -117,39 +151,48 @@ public static function RGBToHSV(int $red, int $green, int $blue): array */ public static function HSVToRGB(int $hue, float $saturation, float $value): array { - if ($hue < 0 || $hue > 360 || - $saturation < 0 || $saturation > 1 || - $value < 0 || $value > 1) { - throw new InvalidArgumentException('Parameters exceed their intended ranges.'); - } + self::validateParameters($hue, $saturation, $value); $chroma = $value * $saturation; $hueNormalized = $hue / 60; $hMod2 = $hueNormalized - 2 * floor($hueNormalized / 2); $secondMax = $chroma * (1 - abs($hMod2 - 1)); + list($r, $g, $b) = self::calculateRGBRange($hueNormalized, $chroma, $secondMax); + + return self::finalizeRGBCalculation($r, $g, $b, $value, $chroma); + } + + private static function validateParameters(int $hue, float $saturation, float $value): void + { + if ($hue < 0 || $hue > 360 || + $saturation < 0 || $saturation > 1 || + $value < 0 || $value > 1) { + throw new InvalidArgumentException('Parameters exceed their intended ranges.'); + } + } + + private static function calculateRGBRange(float $hueNormalized, float $chroma, float $secondMax): array + { if (0 <= $hueNormalized && $hueNormalized < 1) { - list($r, $g, $b) = array($chroma, $secondMax, 0); + return [$chroma, $secondMax, 0]; } elseif (1 <= $hueNormalized && $hueNormalized < 2) { - list($r, $g, $b) = array($secondMax, $chroma, 0); + return [$secondMax, $chroma, 0]; } elseif (2 <= $hueNormalized && $hueNormalized < 3) { - list($r, $g, $b) = array(0, $chroma, $secondMax); + return [0, $chroma, $secondMax]; } elseif (3 <= $hueNormalized && $hueNormalized < 4) { - list($r, $g, $b) = array(0, $secondMax, $chroma); + return [0, $secondMax, $chroma]; } elseif (4 <= $hueNormalized && $hueNormalized < 5) { - list($r, $g, $b) = array($secondMax, 0, $chroma); - } elseif (5 <= $hueNormalized && $hueNormalized < 6) { - list($r, $g, $b) = array($chroma, 0, $secondMax); - } - if (!isset($r) || !isset($g) || !isset($b)) { - throw new Exception('RGB calculation not possible. Check inputs!'); + return [$secondMax, 0, $chroma]; + } else { + return [$chroma, 0, $secondMax]; } - $m = $value - $chroma; - $r = intval(round(($r + $m) * 255)); - $g = intval(round(($g + $m) * 255)); - $b = intval(round(($b + $m) * 255)); + } - return array($r, $g, $b); + private static function finalizeRGBCalculation(float $r, float $g, float $b, float $value, float $chroma): array + { + $m = $value - $chroma; + return array_map(fn($x) => intval(round(($x + $m) * 255)), [$r, $g, $b]); } /** @@ -168,40 +211,6 @@ public static function RGBToHex(int $red, int $green, int $blue): string return $hexr.$hexg.$hexb; } - /** - * Calculate the components Chroma, Value, and Hue based on RGB color. - * - * @param int $red The red component of the RGB color (0-255). - * @param int $green The green component of the RGB color (0-255). - * @param int $blue The blue component of the RGB color (0-255). - * @return array An array containing the calculated values (maxRGB, minRGB, chroma, value, hue). - */ - public static function calculateCVH(int $red, int $green, int $blue): array - { - $normalizedRed = $red / 255; - $normalizedGreen = $green / 255; - $normalizedBlue = $blue / 255; - - $maxRGB = max($normalizedRed, $normalizedGreen, $normalizedBlue); - $minRGB = min($normalizedRed, $normalizedGreen, $normalizedBlue); - $chroma = $maxRGB - $minRGB; - $value = $maxRGB; // also called brightness - if ($chroma == 0) { - $hue = 0; - } elseif ($maxRGB == $normalizedRed) { - $hue = 60 * (($normalizedGreen - $normalizedBlue) / $chroma); - } elseif ($maxRGB == $normalizedGreen) { - $hue = 60 * (2 + ($normalizedBlue - $normalizedRed) / $chroma); - } else { - $hue = 60 * (4 + ($normalizedRed - $normalizedGreen) / $chroma); - } - - if ($hue < 0) { - $hue += 360; - } - return array($maxRGB, $minRGB, $chroma, $value, $hue); - } - /** * Convert HSL color to RGB color space. * @@ -218,60 +227,19 @@ private static function HSLToRGB(int $hue, float $saturation, float $lightness): $hMod2 = $hueNormalized - 2 * floor($hueNormalized / 2); $intermediateValue = $chroma * (1 - abs($hMod2 - 1)); - if (0 <= $hueNormalized && $hueNormalized < 1) { - list($r, $g, $b) = array($chroma, $intermediateValue, 0); - } elseif (1 <= $hueNormalized && $hueNormalized < 2) { - list($r, $g, $b) = array($intermediateValue, $chroma, 0); - } elseif (2 <= $hueNormalized && $hueNormalized < 3) { - list($r, $g, $b) = array(0, $chroma, $intermediateValue); - } elseif (3 <= $hueNormalized && $hueNormalized < 4) { - list($r, $g, $b) = array(0, $intermediateValue, $chroma); - } elseif (4 <= $hueNormalized && $hueNormalized < 5) { - list($r, $g, $b) = array($intermediateValue, 0, $chroma); - } elseif (5 <= $hueNormalized && $hueNormalized < 6) { - list($r, $g, $b) = array($chroma, 0, $intermediateValue); - } - if (!isset($r) || !isset($g) || !isset($b)) { - throw new Exception('RGB calculation not possible. Check inputs!'); - } - $m = $lightness - $chroma / 2; - $r = intval(round(($r + $m) * 255)); - $g = intval(round(($g + $m) * 255)); - $b = intval(round(($b + $m) * 255)); - - return array($r, $g, $b); - } - - /** - * Set the hexadecimal color value. - * - * @param string $color The hexadecimal color value. - * @return void - */ - public function setHex(string $color): void - { - if (!preg_match("/#[0-9a-fA-F]{3}/", $color) && - !preg_match("/#[0-9a-fA-F]{6}/", $color)) { - return; - } - $color = substr($color, 1); - $this->hex = $color; + list($r, $g, $b) = self::calculateRGBRange($hueNormalized, $chroma, $intermediateValue); - $this->rgb = Color::HexToRGB($this->hex); - $this->hsl = Color::RGBToHSL($this->rgb[0], $this->rgb[1], $this->rgb[2]); + return self::finalizeRGBCalculation($r, $g, $b, $lightness, $chroma); } /** - * Set the RGB color. + * Get the HSL color values of the current object. * - * @param array $color An array containing the RGB color values (red, green, blue). - * @return void + * @return array An array containing the HSL color values (hue, saturation, lightness). */ - public function setRGB(array $color): void + public function getHSL(): array { - $this->rgb = $color; - $this->hex = Color::RGBToHex($color[0], $color[1], $color[2]); - $this->hsl = Color::RGBToHSL($color[0], $color[1], $color[2]); + return $this->hsl; } /** @@ -288,23 +256,26 @@ public function setHSL(array $color): void } /** - * Get the HSL color values of the current object. + * Get the RGB color values. * - * @return array An array containing the HSL color values (hue, saturation, lightness). + * @return array An array containing the RGB color values (red, green, blue). */ - public function getHSL(): array + public function getRGB(): array { - return $this->hsl; + return $this->rgb; } /** - * Get the RGB color values. + * Set the RGB color. * - * @return array An array containing the RGB color values (red, green, blue). + * @param array $color An array containing the RGB color values (red, green, blue). + * @return void */ - public function getRGB(): array + public function setRGB(array $color): void { - return $this->rgb; + $this->rgb = $color; + $this->hex = Color::RGBToHex($color[0], $color[1], $color[2]); + $this->hsl = Color::RGBToHSL($color[0], $color[1], $color[2]); } /** @@ -317,6 +288,25 @@ public function getHex(): string return '#'.$this->hex; } + /** + * Set the hexadecimal color value. + * + * @param string $color The hexadecimal color value. + * @return void + */ + public function setHex(string $color): void + { + if (!preg_match("/#[0-9a-fA-F]{3}/", $color) && + !preg_match("/#[0-9a-fA-F]{6}/", $color)) { + return; + } + $color = substr($color, 1); + $this->hex = $color; + + $this->rgb = Color::HexToRGB($this->hex); + $this->hsl = Color::RGBToHSL($this->rgb[0], $this->rgb[1], $this->rgb[2]); + } + /** * Get the color set based on the HSL values of the color. * @@ -350,19 +340,6 @@ public function brighten(int $amount = 10): void $this->setHSL(array($hue, $saturation, $lightness)); } - /** - * Darken the color by reducing its lightness value. - * - * @param int $amount The amount by which to darken the color (0-100). - * @return void - */ - public function darken(int $amount = 10): void - { - list($hue, $saturation, $lightness) = $this->hsl; - $lightness = self::clamp($lightness - $amount / 100, 0, 1); - $this->setHSL(array($hue, $saturation, $lightness)); - } - /** * Clamp a number between a minimum and maximum value. * @@ -381,4 +358,17 @@ private static function clamp(int|float $num, int|float $min, int|float $max): i return $num; } + /** + * Darken the color by reducing its lightness value. + * + * @param int $amount The amount by which to darken the color (0-100). + * @return void + */ + public function darken(int $amount = 10): void + { + list($hue, $saturation, $lightness) = $this->hsl; + $lightness = self::clamp($lightness - $amount / 100, 0, 1); + $this->setHSL(array($hue, $saturation, $lightness)); + } + } \ No newline at end of file