Файл: vendor/intervention/image/src/Colors/Hsl/Colorspace.php
Строк: 246
<?php
declare(strict_types=1);
namespace InterventionImageColorsHsl;
use InterventionImageColorsAbstractColorspace;
use InterventionImageColorsCmykColor as CmykColor;
use InterventionImageColorsHslColor as HslColor;
use InterventionImageColorsHsvColor as HsvColor;
use InterventionImageColorsOklabColor as OklabColor;
use InterventionImageColorsOklchColor as OklchColor;
use InterventionImageColorsRgbColor as RgbColor;
use InterventionImageColorsRgbColorspace as Rgb;
use InterventionImageColorsRgbNamedColor;
use InterventionImageExceptionsColorException;
use InterventionImageExceptionsInvalidArgumentException;
use InterventionImageInterfacesColorChannelInterface;
use InterventionImageInterfacesColorInterface;
use TypeError;
class Colorspace extends AbstractColorspace
{
/**
* Channel class names of colorspace.
*
* @var array<string>
*/
public static array $channels = [
ChannelsHue::class,
ChannelsSaturation::class,
ChannelsLuminance::class,
ChannelsAlpha::class,
];
/**
* {@inheritdoc}
*
* @see ColorspaceInterface::colorFromNormalized()
*
* @throws InvalidArgumentException
*/
public static function colorFromNormalized(array $normalized): HslColor
{
if (!in_array(count($normalized), [3, 4])) {
throw new InvalidArgumentException('Number of color channels must be 3 or 4 for ' . static::class);
}
// add alpha value if missing
$normalized = count($normalized) === 3 ? array_pad($normalized, 4, 1) : $normalized;
return new Color(...array_map(
function (string $channel, null|float $normalized) {
try {
return $channel::fromNormalized($normalized);
} catch (TypeError $e) {
throw new InvalidArgumentException(
'Normalized color value must be in range 0 to 1',
previous: $e,
);
}
},
self::$channels,
$normalized,
));
}
/**
* {@inheritdoc}
*
* @see ColorspaceInterface::importColor()
*
* @throws ColorException
*/
public function importColor(ColorInterface $color): HslColor
{
return match ($color::class) {
HslColor::class => $color,
OklchColor::class,
OklabColor::class,
NamedColor::class,
CmykColor::class => $this->importViaRgbColor($color),
RgbColor::class => $this->importRgbColor($color),
HsvColor::class => $this->importHsvColor($color),
default => throw new ColorException(
'Unable to import color ' . $color::class . ' to ' . $this::class,
),
};
}
/**
* Import given RGB color to HSL colorspace.
*
* @throws ColorException
*/
private function importRgbColor(RgbColor $color): HslColor
{
// normalized values of rgb channels
$values = array_map(
fn(ColorChannelInterface $channel): float => $channel->normalized(),
$color->channels(),
);
// take only RGB
$values = array_slice($values, 0, 3);
// calculate Luminance
$min = min(...$values);
$max = max(...$values);
$luminance = ($max + $min) / 2;
$delta = $max - $min;
// calculate saturation
$saturation = $delta === 0.0 ? 0 : $delta / (1 - abs(2 * $luminance - 1));
// calculate hue
[$r, $g, $b] = $values;
$hue = match (true) {
($delta === 0.0) => 0,
($max === $r) => 60 * fmod((($g - $b) / $delta), 6),
($max === $g) => 60 * ((($b - $r) / $delta) + 2),
($max === $b) => 60 * ((($r - $g) / $delta) + 4),
default => 0,
};
$hue = (round($hue) + 360) % 360; // normalize hue
try {
return new Color(
intval(round($hue)),
intval(round($saturation * 100)),
intval(round($luminance * 100)),
$color->alpha()->normalized(),
);
} catch (InvalidArgumentException $e) {
throw new ColorException(
'Failed to import color ' . $color::class . ' to ' . $this::class,
previous: $e,
);
}
}
/**
* Import given HSV color to HSL colorspace.
*
* @throws ColorException
*/
private function importHsvColor(HsvColor $color): HslColor
{
// normalized values of hsv channels
[$h, $s, $v] = array_map(
fn(ColorChannelInterface $channel): float => $channel->normalized(),
$color->channels(),
);
// calculate Luminance
$luminance = (2.0 - $s) * $v / 2.0;
// calculate Saturation
$saturation = match (true) {
$luminance === 0.0 => $s,
$luminance === 1.0 => 0,
$luminance < .5 => $s * $v / ($luminance * 2),
default => $s * $v / (2 - $luminance * 2),
};
try {
return new Color(
intval(round($h * 360)),
intval(round($saturation * 100)),
intval(round($luminance * 100)),
$color->alpha()->normalized(),
);
} catch (InvalidArgumentException $e) {
throw new ColorException(
'Failed to import color ' . $color::class . ' to ' . $this::class,
previous: $e,
);
}
}
/**
* Import given color to HSL color space by converting it to RGB first.
*
* @throws ColorException
*/
private function importViaRgbColor(NamedColor|CmykColor|OklabColor|OklchColor $color): HslColor
{
try {
$color = $color->toColorspace(Rgb::class);
} catch (InvalidArgumentException $e) {
throw new ColorException(
'Failed to import color ' . $color::class . ' to ' . $this::class,
previous: $e,
);
}
if (!$color instanceof RgbColor) {
throw new ColorException('Failed to import color ' . $color::class . ' to ' . $this::class);
}
return $this->importRgbColor($color);
}
}