diff --git a/src/Drivers/Gd/Analyzers/PixelColorAnalyzer.php b/src/Drivers/Gd/Analyzers/PixelColorAnalyzer.php index b27583a3c..80542f3cb 100644 --- a/src/Drivers/Gd/Analyzers/PixelColorAnalyzer.php +++ b/src/Drivers/Gd/Analyzers/PixelColorAnalyzer.php @@ -36,6 +36,10 @@ protected function colorAt(ColorspaceInterface $colorspace, GdImage $gd): ColorI { $index = @imagecolorat($gd, $this->x, $this->y); + if (!imageistruecolor($gd)) { + $index = imagecolorsforindex($gd, $index); + } + if ($index === false) { throw new GeometryException( 'The specified position is not in the valid image area.' diff --git a/src/Drivers/Gd/ColorProcessor.php b/src/Drivers/Gd/ColorProcessor.php index 305837390..935b57ac1 100644 --- a/src/Drivers/Gd/ColorProcessor.php +++ b/src/Drivers/Gd/ColorProcessor.php @@ -25,6 +25,7 @@ class ColorProcessor implements ColorProcessorInterface */ public function __construct(protected ColorspaceInterface $colorspace = new Colorspace()) { + // } /** @@ -57,14 +58,29 @@ public function colorToNative(ColorInterface $color): int */ public function nativeToColor(mixed $value): ColorInterface { - if (!is_int($value)) { - throw new ColorException('GD driver can only decode colors in integer format.'); + if (!is_int($value) && !is_array($value)) { + throw new ColorException('GD driver can only decode colors in integer and array format.'); } - $a = ($value >> 24) & 0xFF; - $r = ($value >> 16) & 0xFF; - $g = ($value >> 8) & 0xFF; - $b = $value & 0xFF; + if (is_array($value)) { + // array conversion + if (!$this->isValidArrayColor($value)) { + throw new ColorException( + 'GD driver can only decode array color format array{red: int, green: int, blue: int, alpha: int}.', + ); + } + + $r = $value['red']; + $g = $value['green']; + $b = $value['blue']; + $a = $value['alpha']; + } else { + // integer conversion + $a = ($value >> 24) & 0xFF; + $r = ($value >> 16) & 0xFF; + $g = ($value >> 8) & 0xFF; + $b = $value & 0xFF; + } // convert gd apha integer to intervention alpha integer // ([opaque]0-127[transparent]) to ([opaque]255-0[transparent]) @@ -93,4 +109,49 @@ protected function convertRange( ): float|int { return ceil(((($input - $min) * ($targetMax - $targetMin)) / ($max - $min)) + $targetMin); } + + /** + * Check if given array is valid color format + * array{red: int, green: int, blue: int, alpha: int} + * i.e. result of imagecolorsforindex() + * + * @param array $color + * @return bool + */ + private function isValidArrayColor(array $color): bool + { + if (!array_key_exists('red', $color)) { + return false; + } + + if (!array_key_exists('green', $color)) { + return false; + } + + if (!array_key_exists('blue', $color)) { + return false; + } + + if (!array_key_exists('alpha', $color)) { + return false; + } + + if (!is_int($color['red'])) { + return false; + } + + if (!is_int($color['green'])) { + return false; + } + + if (!is_int($color['blue'])) { + return false; + } + + if (!is_int($color['alpha'])) { + return false; + } + + return true; + } } diff --git a/tests/Unit/Drivers/Gd/ColorProcessorTest.php b/tests/Unit/Drivers/Gd/ColorProcessorTest.php index 9d574360b..80f079ee4 100644 --- a/tests/Unit/Drivers/Gd/ColorProcessorTest.php +++ b/tests/Unit/Drivers/Gd/ColorProcessorTest.php @@ -26,7 +26,7 @@ public function testColorToNative(): void $this->assertEquals(16725760, $result); } - public function testNativeToColor(): void + public function testNativeToColorInteger(): void { $processor = new ColorProcessor(); $result = $processor->nativeToColor(16725760); @@ -37,6 +37,17 @@ public function testNativeToColor(): void $this->assertEquals(255, $result->channel(Alpha::class)->value()); } + public function testNativeToColorArray(): void + { + $processor = new ColorProcessor(); + $result = $processor->nativeToColor(['red' => 255, 'green' => 55, 'blue' => 0, 'alpha' => 0]); + $this->assertInstanceOf(Color::class, $result); + $this->assertEquals(255, $result->channel(Red::class)->value()); + $this->assertEquals(55, $result->channel(Green::class)->value()); + $this->assertEquals(0, $result->channel(Blue::class)->value()); + $this->assertEquals(255, $result->channel(Alpha::class)->value()); + } + public function testNativeToColorInvalid(): void { $processor = new ColorProcessor(); diff --git a/tests/Unit/Drivers/Gd/Modifiers/QuantizeColorsModifierTest.php b/tests/Unit/Drivers/Gd/Modifiers/QuantizeColorsModifierTest.php index 73d253fc5..f13dec9cb 100644 --- a/tests/Unit/Drivers/Gd/Modifiers/QuantizeColorsModifierTest.php +++ b/tests/Unit/Drivers/Gd/Modifiers/QuantizeColorsModifierTest.php @@ -55,4 +55,11 @@ private function assertColorCount(int $count, ImageInterface $image): void $this->assertEquals(count($colors), $count); } + + public function testVerifyColorValueAfterQuantization(): void + { + $image = $this->createTestImage(3, 2)->fill('f00'); + $image->modify(new QuantizeColorsModifier(1)); + $this->assertColor(255, 0, 0, 255, $image->pickColor(1, 1), 4); + } } diff --git a/tests/Unit/Drivers/Imagick/Modifiers/QuantizeColorsModifierTest.php b/tests/Unit/Drivers/Imagick/Modifiers/QuantizeColorsModifierTest.php index e6c9d5efe..917771b5e 100644 --- a/tests/Unit/Drivers/Imagick/Modifiers/QuantizeColorsModifierTest.php +++ b/tests/Unit/Drivers/Imagick/Modifiers/QuantizeColorsModifierTest.php @@ -37,4 +37,11 @@ public function testInvalidColorInput(): void $this->expectException(InputException::class); $image->modify(new QuantizeColorsModifier(0)); } + + public function testVerifyColorValueAfterQuantization(): void + { + $image = $this->createTestImage(3, 2)->fill('f00'); + $image->modify(new QuantizeColorsModifier(1)); + $this->assertColor(255, 0, 0, 255, $image->pickColor(1, 1)); + } }