diff --git a/src/Colors/AbstractColorChannel.php b/src/Colors/AbstractColorChannel.php new file mode 100644 index 00000000..a5777714 --- /dev/null +++ b/src/Colors/AbstractColorChannel.php @@ -0,0 +1,91 @@ +value = $this->validate( + match (true) { + is_null($value) && is_numeric($normalized) => intval(round($normalized * $this->max())), + is_numeric($value) && is_null($normalized) => $value, + default => throw new ColorException('Color channels must either have a value or a normalized value') + } + ); + } + + /** + * Alias of value() + * + * @return int + */ + public function toInt(): int + { + return $this->value; + } + + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::value() + */ + public function value(): int + { + return $this->value; + } + + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::normalize() + */ + public function normalize($precision = 32): float + { + return round($this->value() / $this->max(), $precision); + } + + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::validate() + */ + public function validate(mixed $value): mixed + { + if ($value < $this->min() || $value > $this->max()) { + throw new ColorException('Color channel value must be in range ' . $this->min() . ' to ' . $this->max()); + } + + return $value; + } + + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::toString() + */ + public function toString(): string + { + return (string) $this->value(); + } + + /** + * {@inheritdoc} + * + * @see ColorChannelInterface::__toString() + */ + public function __toString(): string + { + return $this->toString(); + } +} diff --git a/src/Colors/Cmyk/Channels/Cyan.php b/src/Colors/Cmyk/Channels/Cyan.php index 8da3a429..d0ac4612 100644 --- a/src/Colors/Cmyk/Channels/Cyan.php +++ b/src/Colors/Cmyk/Channels/Cyan.php @@ -2,39 +2,10 @@ namespace Intervention\Image\Colors\Cmyk\Channels; -use Intervention\Image\Exceptions\ColorException; -use Intervention\Image\Interfaces\ColorChannelInterface; +use Intervention\Image\Colors\AbstractColorChannel; -class Cyan implements ColorChannelInterface +class Cyan extends AbstractColorChannel { - protected int $value; - - /** - * {@inheritdoc} - * - * @see ColorChannelInterface::__construct() - */ - public function __construct(int $value = null, float $normalized = null) - { - $this->value = $this->validate( - match (true) { - is_null($value) && is_numeric($normalized) => intval(round($normalized * $this->max())), - is_numeric($value) && is_null($normalized) => $value, - default => throw new ColorException('Color channels must either have a value or a normalized value') - } - ); - } - - public function value(): int - { - return $this->value; - } - - public function normalize($precision = 32): float - { - return round($this->value() / $this->max(), $precision); - } - public function min(): int { return 0; @@ -44,23 +15,4 @@ class Cyan implements ColorChannelInterface { return 100; } - - public function validate(mixed $value): mixed - { - if ($value < $this->min() || $value > $this->max()) { - throw new ColorException('CMYK color values must be in range 0-100.'); - } - - return $value; - } - - public function toString(): string - { - return (string) $this->value(); - } - - public function __toString(): string - { - return $this->toString(); - } } diff --git a/src/Colors/Cmyk/Colorspace.php b/src/Colors/Cmyk/Colorspace.php index c05749b5..44115449 100644 --- a/src/Colors/Cmyk/Colorspace.php +++ b/src/Colors/Cmyk/Colorspace.php @@ -4,6 +4,8 @@ namespace Intervention\Image\Colors\Cmyk; use Intervention\Image\Colors\Rgb\Color as RgbColor; use Intervention\Image\Colors\Cmyk\Color as CmykColor; +use Intervention\Image\Colors\Hsv\Color as HsvColor; +use Intervention\Image\Colors\Rgb\Colorspace as RgbColorspace; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -39,6 +41,7 @@ class Colorspace implements ColorspaceInterface { return match (get_class($color)) { RgbColor::class => $this->convertRgbColor($color), + HsvColor::class => $this->convertRgbColor($color->convertTo(RgbColorspace::class)), default => $color, }; } diff --git a/src/Colors/Hsv/Channels/Hue.php b/src/Colors/Hsv/Channels/Hue.php new file mode 100644 index 00000000..d578e54d --- /dev/null +++ b/src/Colors/Hsv/Channels/Hue.php @@ -0,0 +1,28 @@ +channels = [ + new Hue($h), + new Saturation($s), + new Value($v), + ]; + } + + public function colorspace(): ColorspaceInterface + { + return new Colorspace(); + } + + /** + * {@inheritdoc} + * + * @see ColorInterface::create() + */ + public static function create(mixed $input): ColorInterface + { + return (new class ([ + Decoders\StringColorDecoder::class, + ]) extends AbstractInputHandler + { + })->handle($input); + } + + /** + * Return the Hue channel + * + * @return ColorChannelInterface + */ + public function hue(): ColorChannelInterface + { + return $this->channel(Hue::class); + } + + /** + * Return the Saturation channel + * + * @return ColorChannelInterface + */ + public function saturation(): ColorChannelInterface + { + return $this->channel(Saturation::class); + } + + /** + * Return the Value channel + * + * @return ColorChannelInterface + */ + public function value(): ColorChannelInterface + { + return $this->channel(Value::class); + } + + /** + * {@inheritdoc} + * + * @see ColorInterface::toArray() + */ + public function toArray(): array + { + return array_map(function (ColorChannelInterface $channel) { + return $channel->value(); + }, $this->channels()); + } + + /** + * {@inheritdoc} + * + * @see ColorInterface::convertTo() + */ + public function convertTo(string|ColorspaceInterface $colorspace): ColorInterface + { + $colorspace = match (true) { + is_object($colorspace) => $colorspace, + default => new $colorspace(), + }; + + return $colorspace->convertColor($this); + } + + public function toHex(string $prefix = ''): string + { + return $this->convertTo(RgbColorspace::class)->toHex($prefix); + } + + /** + * {@inheritdoc} + * + * @see ColorInterface::toString() + */ + public function toString(): string + { + return sprintf( + 'hsv(%d, %d%%, %d%%)', + $this->hue()->value(), + $this->saturation()->value(), + $this->value()->value() + ); + } + + /** + * {@inheritdoc} + * + * @see ColorInterface::isGreyscale() + */ + public function isGreyscale(): bool + { + return $this->saturation()->value() == 0; + } + + /** + * {@inheritdoc} + * + * @see ColorInterface::__toString() + */ + public function __toString(): string + { + return $this->toString(); + } +} diff --git a/src/Colors/Hsv/Colorspace.php b/src/Colors/Hsv/Colorspace.php new file mode 100644 index 00000000..af454f99 --- /dev/null +++ b/src/Colors/Hsv/Colorspace.php @@ -0,0 +1,77 @@ +value(); + }, self::$channels, $normalized); + + return new Color(...$values); + } + + public function convertColor(ColorInterface $color): ColorInterface + { + return match (get_class($color)) { + CmykColor::class => $this->convertRgbColor($color->convertTo(RgbColorspace::class)), + RgbColor::class => $this->convertRgbColor($color), + default => $color, + }; + } + + protected function convertRgbColor(RgbColor $color): Color + { + // percentage values of rgb channels + $values = array_map(function ($channel) { + return $channel->normalize(); + }, $color->channels()); + + // take only RGB + $values = array_slice($values, 0, 3); + + // calculate chroma + $min = min(...$values); + $max = max(...$values); + $chroma = $max - $min; + + // calculate value + $v = 100 * $max; + + // calculate saturation + $s = 100 * ($chroma / $max); + + // calculate hue + list($r, $g, $b) = $values; + $h = match (true) { + ($r == $min) => 3 - (($g - $b) / $chroma), + ($b == $min) => 1 - (($r - $g) / $chroma), + default => 5 - (($b - $r) / $chroma), + } * 60; + + return new Color( + intval(round($h)), + intval(round($s)), + intval(round($v)) + ); + } +} diff --git a/src/Colors/Hsv/Decoders/StringColorDecoder.php b/src/Colors/Hsv/Decoders/StringColorDecoder.php new file mode 100644 index 00000000..395396b9 --- /dev/null +++ b/src/Colors/Hsv/Decoders/StringColorDecoder.php @@ -0,0 +1,40 @@ +[0-9\.]+), ?(?P[0-9\.]+%?), ?(?P[0-9\.]+%?)?\)$/i'; + if (preg_match($pattern, $input, $matches) != 1) { + throw new DecoderException('Unable to decode input'); + } + + $values = array_map(function ($value) { + return match (strpos($value, '%')) { + false => intval(trim($value)), + default => intval(trim(str_replace('%', '', $value))), + }; + }, [$matches['h'], $matches['s'], $matches['v']]); + + return new Color(...$values); + } +} diff --git a/src/Colors/Rgb/Channels/Alpha.php b/src/Colors/Rgb/Channels/Alpha.php index 7172791f..3d743c0e 100644 --- a/src/Colors/Rgb/Channels/Alpha.php +++ b/src/Colors/Rgb/Channels/Alpha.php @@ -8,9 +8,4 @@ class Alpha extends Red { return strval(round($this->normalize(), 6)); } - - public function __toString(): string - { - return $this->toString(); - } } diff --git a/src/Colors/Rgb/Channels/Red.php b/src/Colors/Rgb/Channels/Red.php index 68c3e130..0e18bb30 100644 --- a/src/Colors/Rgb/Channels/Red.php +++ b/src/Colors/Rgb/Channels/Red.php @@ -2,59 +2,10 @@ namespace Intervention\Image\Colors\Rgb\Channels; -use Intervention\Image\Exceptions\ColorException; -use Intervention\Image\Interfaces\ColorChannelInterface; +use Intervention\Image\Colors\AbstractColorChannel; -class Red implements ColorChannelInterface +class Red extends AbstractColorChannel { - protected int $value; - - /** - * {@inheritdoc} - * - * @see ColorChannelInterface::__construct() - */ - public function __construct(int $value = null, float $normalized = null) - { - $this->value = $this->validate( - match (true) { - is_null($value) && is_numeric($normalized) => intval(round($normalized * $this->max())), - is_numeric($value) && is_null($normalized) => $value, - default => throw new ColorException('Color channels must either have a value or a normalized value') - } - ); - } - - /** - * Alias of value() - * - * @return int - */ - public function toInt(): int - { - return $this->value; - } - - /** - * {@inheritdoc} - * - * @see ColorChannelInterface::value() - */ - public function value(): int - { - return $this->value; - } - - /** - * {@inheritdoc} - * - * @see ColorChannelInterface::normalize() - */ - public function normalize($precision = 32): float - { - return round($this->value() / $this->max(), $precision); - } - /** * {@inheritdoc} * @@ -74,38 +25,4 @@ class Red implements ColorChannelInterface { return 255; } - - /** - * {@inheritdoc} - * - * @see ColorChannelInterface::validate() - */ - public function validate(mixed $value): mixed - { - if ($value < $this->min() || $value > $this->max()) { - throw new ColorException('RGB color values must be in range 0-255.'); - } - - return $value; - } - - /** - * {@inheritdoc} - * - * @see ColorChannelInterface::toString() - */ - public function toString(): string - { - return (string) $this->value(); - } - - /** - * {@inheritdoc} - * - * @see ColorChannelInterface::__toString() - */ - public function __toString(): string - { - return $this->toString(); - } } diff --git a/src/Colors/Rgb/Colorspace.php b/src/Colors/Rgb/Colorspace.php index f3dd1503..54ca8bb2 100644 --- a/src/Colors/Rgb/Colorspace.php +++ b/src/Colors/Rgb/Colorspace.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Colors\Rgb; +use Intervention\Image\Colors\Hsv\Color as HsvColor; use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; @@ -18,7 +19,7 @@ class Colorspace implements ColorspaceInterface /** * {@inheritdoc} * - * @see ColorspaceInterface::createColor() + * @see ColorspaceInterface::colorFromNormalized() */ public function colorFromNormalized(array $normalized): ColorInterface { @@ -29,10 +30,16 @@ class Colorspace implements ColorspaceInterface return new Color(...$values); } + /** + * {@inheritdoc} + * + * @see ColorspaceInterface::convertColor() + */ public function convertColor(ColorInterface $color): ColorInterface { return match (get_class($color)) { CmykColor::class => $this->convertCmykColor($color), + HsvColor::class => $this->convertHsvColor($color), default => $color, }; } @@ -45,4 +52,30 @@ class Colorspace implements ColorspaceInterface (int) (255 * (1 - $color->yellow()->normalize()) * (1 - $color->key()->normalize())), ); } + + protected function convertHsvColor(HsvColor $color): Color + { + $chroma = $color->value()->normalize() * $color->saturation()->normalize(); + $hue = $color->hue()->normalize() * 6; + $x = $chroma * (1 - abs(fmod($hue, 2) - 1)); + + // connect channel values + $values = match (true) { + $hue < 1 => [$chroma, $x, 0], + $hue < 2 => [$x, $chroma, 0], + $hue < 3 => [0, $chroma, $x], + $hue < 4 => [0, $x, $chroma], + $hue < 5 => [$x, 0, $chroma], + default => [$chroma, 0, $x], + }; + + // add to each value + $values = array_map(function ($value) use ($color, $chroma) { + return $value + ($color->value()->normalize() - $chroma); + }, $values); + + array_push($values, 1); // append alpha channel value + + return $this->colorFromNormalized($values); + } } diff --git a/src/Drivers/Gd/InputHandler.php b/src/Drivers/Gd/InputHandler.php index 758b9d1f..96a0e162 100644 --- a/src/Drivers/Gd/InputHandler.php +++ b/src/Drivers/Gd/InputHandler.php @@ -7,6 +7,7 @@ use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder as RgbStringColorD use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; use Intervention\Image\Colors\Rgb\Decoders\TransparentColorDecoder; use Intervention\Image\Colors\Cmyk\Decoders\StringColorDecoder as CmykStringColorDecoder; +use Intervention\Image\Colors\Hsv\Decoders\StringColorDecoder as HsvStringColorDecoder; use Intervention\Image\Drivers\AbstractInputHandler; use Intervention\Image\Drivers\Gd\Decoders\ImageObjectDecoder; use Intervention\Image\Drivers\Gd\Decoders\ColorObjectDecoder; @@ -25,6 +26,7 @@ class InputHandler extends AbstractInputHandler RgbHexColorDecoder::class, RgbStringColorDecoder::class, CmykStringColorDecoder::class, + HsvStringColorDecoder::class, TransparentColorDecoder::class, HtmlColornameDecoder::class, FilePointerImageDecoder::class, diff --git a/src/Drivers/Imagick/InputHandler.php b/src/Drivers/Imagick/InputHandler.php index b03d079a..7afaa2a9 100644 --- a/src/Drivers/Imagick/InputHandler.php +++ b/src/Drivers/Imagick/InputHandler.php @@ -7,6 +7,7 @@ use Intervention\Image\Colors\Rgb\Decoders\StringColorDecoder as RgbStringColorD use Intervention\Image\Colors\Rgb\Decoders\HtmlColornameDecoder; use Intervention\Image\Colors\Rgb\Decoders\TransparentColorDecoder; use Intervention\Image\Colors\Cmyk\Decoders\StringColorDecoder as CmykStringColorDecoder; +use Intervention\Image\Colors\Hsv\Decoders\StringColorDecoder as HsvStringColorDecoder; use Intervention\Image\Drivers\AbstractInputHandler; use Intervention\Image\Drivers\Imagick\Decoders\ImageObjectDecoder; use Intervention\Image\Drivers\Imagick\Decoders\ColorObjectDecoder; @@ -25,6 +26,7 @@ class InputHandler extends AbstractInputHandler RgbHexColorDecoder::class, RgbStringColorDecoder::class, CmykStringColorDecoder::class, + HsvStringColorDecoder::class, TransparentColorDecoder::class, HtmlColornameDecoder::class, FilePointerImageDecoder::class, diff --git a/tests/Colors/Cmyk/ColorspaceTest.php b/tests/Colors/Cmyk/ColorspaceTest.php index 818269fc..65e67a78 100644 --- a/tests/Colors/Cmyk/ColorspaceTest.php +++ b/tests/Colors/Cmyk/ColorspaceTest.php @@ -2,8 +2,13 @@ namespace Intervention\Image\Tests\Colors\Cmyk; +use Intervention\Image\Colors\Cmyk\Channels\Cyan; +use Intervention\Image\Colors\Cmyk\Channels\Key; +use Intervention\Image\Colors\Cmyk\Channels\Magenta; +use Intervention\Image\Colors\Cmyk\Channels\Yellow; use Intervention\Image\Colors\Cmyk\Color as CmykColor; use Intervention\Image\Colors\Rgb\Color as RgbColor; +use Intervention\Image\Colors\Hsv\Color as HsvColor; use Intervention\Image\Colors\Cmyk\Colorspace; use Intervention\Image\Tests\TestCase; @@ -13,14 +18,25 @@ use Intervention\Image\Tests\TestCase; */ class ColorspaceTest extends TestCase { - public function testConvertColor(): void + public function testConvertRgbColor(): void { $colorspace = new Colorspace(); - $this->assertInstanceOf( - CmykColor::class, - $colorspace->convertColor( - new RgbColor(0, 0, 0) - ) - ); + $result = $colorspace->convertColor(new RgbColor(0, 0, 255)); + $this->assertInstanceOf(CmykColor::class, $result); + $this->assertEquals(100, $result->channel(Cyan::class)->value()); + $this->assertEquals(100, $result->channel(Magenta::class)->value()); + $this->assertEquals(0, $result->channel(Yellow::class)->value()); + $this->assertEquals(0, $result->channel(Key::class)->value()); + } + + public function testConvertHsvColor(): void + { + $colorspace = new Colorspace(); + $result = $colorspace->convertColor(new HsvColor(240, 100, 100)); + $this->assertInstanceOf(CmykColor::class, $result); + $this->assertEquals(100, $result->channel(Cyan::class)->value()); + $this->assertEquals(100, $result->channel(Magenta::class)->value()); + $this->assertEquals(0, $result->channel(Yellow::class)->value()); + $this->assertEquals(0, $result->channel(Key::class)->value()); } } diff --git a/tests/Colors/Hsv/ChannelTest.php b/tests/Colors/Hsv/ChannelTest.php new file mode 100644 index 00000000..2e463c53 --- /dev/null +++ b/tests/Colors/Hsv/ChannelTest.php @@ -0,0 +1,75 @@ +assertInstanceOf(Hue::class, $channel); + + $channel = new Hue(value: 0); + $this->assertInstanceOf(Hue::class, $channel); + + $channel = new Hue(normalized: 0); + $this->assertInstanceOf(Hue::class, $channel); + + $this->expectException(ColorException::class); + $channel = new Hue(); + + $this->expectException(ColorException::class); + $channel = new Hue(normalized: 2); + } + + public function testValue(): void + { + $channel = new Hue(10); + $this->assertEquals(10, $channel->value()); + } + + public function testNormalize(): void + { + $channel = new Hue(360); + $this->assertEquals(1, $channel->normalize()); + $channel = new Hue(180); + $this->assertEquals(0.5, $channel->normalize()); + $channel = new Hue(0); + $this->assertEquals(0, $channel->normalize()); + $channel = new Hue(90); + $this->assertEquals(.25, $channel->normalize()); + } + + public function testValidate(): void + { + $this->expectException(ColorException::class); + new Hue(361); + + $this->expectException(ColorException::class); + new Hue(-1); + + $this->expectException(ColorException::class); + new Saturation(101); + + $this->expectException(ColorException::class); + new Saturation(-1); + + $this->expectException(ColorException::class); + new Value(101); + + $this->expectException(ColorException::class); + new Value(-1); + } +} diff --git a/tests/Colors/Hsv/ColorTest.php b/tests/Colors/Hsv/ColorTest.php new file mode 100644 index 00000000..3fc70b09 --- /dev/null +++ b/tests/Colors/Hsv/ColorTest.php @@ -0,0 +1,84 @@ +assertInstanceOf(Color::class, $color); + } + + public function testColorspace(): void + { + $color = new Color(0, 0, 0); + $this->assertInstanceOf(Colorspace::class, $color->colorspace()); + } + + public function testChannels(): void + { + $color = new Color(10, 20, 30); + $this->assertIsArray($color->channels()); + $this->assertCount(3, $color->channels()); + } + + public function testChannel(): void + { + $color = new Color(10, 20, 30); + $channel = $color->channel(Hue::class); + $this->assertInstanceOf(Hue::class, $channel); + $this->assertEquals(10, $channel->value()); + } + + public function testHueSaturationValueKey(): void + { + $color = new Color(10, 20, 30); + $this->assertInstanceOf(Hue::class, $color->hue()); + $this->assertInstanceOf(Saturation::class, $color->saturation()); + $this->assertInstanceOf(Value::class, $color->value()); + $this->assertEquals(10, $color->hue()->value()); + $this->assertEquals(20, $color->saturation()->value()); + $this->assertEquals(30, $color->value()->value()); + } + + public function testToArray(): void + { + $color = new Color(10, 20, 30); + $this->assertEquals([10, 20, 30], $color->toArray()); + } + + public function testNormalize(): void + { + $color = new Color(180, 50, 25); + $this->assertEquals([.5, 0.5, 0.25], $color->normalize()); + } + + public function testToString(): void + { + $color = new Color(100, 50, 20, 0); + $this->assertEquals('hsv(100, 50%, 20%)', (string) $color); + } + + public function testIsGreyscale(): void + { + $color = new Color(0, 1, 0); + $this->assertFalse($color->isGreyscale()); + + $color = new Color(1, 0, 0); + $this->assertTrue($color->isGreyscale()); + + $color = new Color(0, 0, 1); + $this->assertTrue($color->isGreyscale()); + } +} diff --git a/tests/Colors/Hsv/ColorspaceTest.php b/tests/Colors/Hsv/ColorspaceTest.php new file mode 100644 index 00000000..48c77e1d --- /dev/null +++ b/tests/Colors/Hsv/ColorspaceTest.php @@ -0,0 +1,39 @@ +convertColor(new RgbColor(26, 26, 128)); + $this->assertInstanceOf(HsvColor::class, $result); + $this->assertEquals(240, $result->channel(Hue::class)->value()); + $this->assertEquals(80, $result->channel(Saturation::class)->value()); + $this->assertEquals(50, $result->channel(Value::class)->value()); + } + + public function testConvertCmykColor(): void + { + $colorspace = new Colorspace(); + $result = $colorspace->convertColor(new CmykColor(100, 0, 100, 0)); + $this->assertInstanceOf(HsvColor::class, $result); + $this->assertEquals(120, $result->channel(Hue::class)->value()); + $this->assertEquals(100, $result->channel(Saturation::class)->value()); + $this->assertEquals(100, $result->channel(Value::class)->value()); + } +} diff --git a/tests/Colors/Hsv/Decoders/StringColorDecoderTest.php b/tests/Colors/Hsv/Decoders/StringColorDecoderTest.php new file mode 100644 index 00000000..6aa95c89 --- /dev/null +++ b/tests/Colors/Hsv/Decoders/StringColorDecoderTest.php @@ -0,0 +1,56 @@ +decode('hsv(0,0,0)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 0, 0], $result->toArray()); + + $result = $decoder->decode('hsv(0, 100, 100)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 100, 100], $result->toArray()); + + $result = $decoder->decode('hsv(360, 100, 100)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([360, 100, 100], $result->toArray()); + + + $result = $decoder->decode('hsv(180, 100%, 100%)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([180, 100, 100], $result->toArray()); + } + + public function testDecodeHsb(): void + { + $decoder = new StringColorDecoder(); + $result = $decoder->decode('hsb(0,0,0)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 0, 0], $result->toArray()); + + $result = $decoder->decode('hsb(0, 100, 100)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([0, 100, 100], $result->toArray()); + + $result = $decoder->decode('hsb(360, 100, 100)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([360, 100, 100], $result->toArray()); + + + $result = $decoder->decode('hsb(180, 100%, 100%)'); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals([180, 100, 100], $result->toArray()); + } +} diff --git a/tests/Colors/Rgb/ColorspaceTest.php b/tests/Colors/Rgb/ColorspaceTest.php index be7808c5..849e20b0 100644 --- a/tests/Colors/Rgb/ColorspaceTest.php +++ b/tests/Colors/Rgb/ColorspaceTest.php @@ -3,6 +3,10 @@ namespace Intervention\Image\Tests\Colors\Rgb; use Intervention\Image\Colors\Cmyk\Color as CmykColor; +use Intervention\Image\Colors\Hsv\Color as HsvColor; +use Intervention\Image\Colors\Rgb\Channels\Blue; +use Intervention\Image\Colors\Rgb\Channels\Green; +use Intervention\Image\Colors\Rgb\Channels\Red; use Intervention\Image\Colors\Rgb\Color as RgbColor; use Intervention\Image\Colors\Rgb\Colorspace; use Intervention\Image\Tests\TestCase; @@ -13,7 +17,7 @@ use Intervention\Image\Tests\TestCase; */ class ColorspaceTest extends TestCase { - public function testConvertColor(): void + public function testConvertCmykColor(): void { $colorspace = new Colorspace(); $this->assertInstanceOf( @@ -23,4 +27,14 @@ class ColorspaceTest extends TestCase ) ); } + + public function testConvertHsvColor(): void + { + $colorspace = new Colorspace(); + $result = $colorspace->convertColor(new HsvColor(240, 80, 50)); + $this->assertInstanceOf(RgbColor::class, $result); + $this->assertEquals(26, $result->channel(Red::class)->value()); + $this->assertEquals(26, $result->channel(Green::class)->value()); + $this->assertEquals(128, $result->channel(Blue::class)->value()); + } } diff --git a/tests/Drivers/Gd/InputHandlerTest.php b/tests/Drivers/Gd/InputHandlerTest.php index 29fcb018..1857b20e 100644 --- a/tests/Drivers/Gd/InputHandlerTest.php +++ b/tests/Drivers/Gd/InputHandlerTest.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Tests\Drivers\Gd; +use Intervention\Image\Colors\Cmyk\Color as CmykColor; +use Intervention\Image\Colors\Hsv\Color as HsvColor; use Intervention\Image\Colors\Rgb\Color as RgbColor; use Intervention\Image\Image; use Intervention\Image\Drivers\Gd\InputHandler; @@ -114,6 +116,22 @@ class GdInputHandlerTest extends TestCase $this->assertEquals([10, 20, 30, 255], $result->toArray()); } + public function testHandleHsvString(): void + { + $handler = new InputHandler(); + $result = $handler->handle('hsv(10, 20, 30)'); + $this->assertInstanceOf(HsvColor::class, $result); + $this->assertEquals([10, 20, 30], $result->toArray()); + } + + public function testHandleCmykString(): void + { + $handler = new InputHandler(); + $result = $handler->handle('cmyk(10, 20, 30, 40)'); + $this->assertInstanceOf(CmykColor::class, $result); + $this->assertEquals([10, 20, 30, 40], $result->toArray()); + } + public function testHandleTransparent(): void { $handler = new InputHandler(); diff --git a/tests/Drivers/Imagick/InputHandlerTest.php b/tests/Drivers/Imagick/InputHandlerTest.php index b56f3548..1175858a 100644 --- a/tests/Drivers/Imagick/InputHandlerTest.php +++ b/tests/Drivers/Imagick/InputHandlerTest.php @@ -2,12 +2,14 @@ namespace Intervention\Image\Tests\Drivers\Imagick; -use SplFileInfo; +use Intervention\Image\Colors\Cmyk\Color as CmykColor; +use Intervention\Image\Colors\Hsv\Color as HsvColor; use Intervention\Image\Colors\Rgb\Color as RgbColor; -use Intervention\Image\Image; use Intervention\Image\Drivers\Imagick\InputHandler; use Intervention\Image\Exceptions\DecoderException; +use Intervention\Image\Image; use Intervention\Image\Tests\TestCase; +use SplFileInfo; /** * @requires extension imagick @@ -114,6 +116,22 @@ class InputHandlerTest extends TestCase $this->assertEquals([10, 20, 30, 255], $result->toArray()); } + public function testHandleCmykString(): void + { + $handler = new InputHandler(); + $result = $handler->handle('cmyk(10, 20, 30, 40)'); + $this->assertInstanceOf(CmykColor::class, $result); + $this->assertEquals([10, 20, 30, 40], $result->toArray()); + } + + public function testHandleHsvString(): void + { + $handler = new InputHandler(); + $result = $handler->handle('hsv(10, 20, 30)'); + $this->assertInstanceOf(HsvColor::class, $result); + $this->assertEquals([10, 20, 30], $result->toArray()); + } + public function testHandleTransparent(): void { $handler = new InputHandler();