1
0
mirror of https://github.com/Intervention/image.git synced 2025-08-18 19:51:22 +02:00

Fix incorrect color results after color reduction (#1410)

* Add tests to verify issue 1409
* Enable GD's ColorProcessor to resolve array color format
* Fix bug when reading colors from palette GDImage
* Add detailed type hint
This commit is contained in:
Oliver Vogel
2025-01-04 08:31:37 +01:00
committed by GitHub
parent 436460e33b
commit 1c68e5fdf4
5 changed files with 97 additions and 7 deletions

View File

@@ -36,6 +36,10 @@ class PixelColorAnalyzer extends GenericPixelColorAnalyzer implements Specialize
{
$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.'

View File

@@ -25,6 +25,7 @@ class ColorProcessor implements ColorProcessorInterface
*/
public function __construct(protected ColorspaceInterface $colorspace = new Colorspace())
{
//
}
/**
@@ -57,14 +58,29 @@ class ColorProcessor implements ColorProcessorInterface
*/
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 @@ class ColorProcessor implements ColorProcessorInterface
): 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<mixed> $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;
}
}

View File

@@ -26,7 +26,7 @@ final class ColorProcessorTest extends BaseTestCase
$this->assertEquals(16725760, $result);
}
public function testNativeToColor(): void
public function testNativeToColorInteger(): void
{
$processor = new ColorProcessor();
$result = $processor->nativeToColor(16725760);
@@ -37,6 +37,17 @@ final class ColorProcessorTest extends BaseTestCase
$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();

View File

@@ -55,4 +55,11 @@ final class QuantizeColorsModifierTest extends GdTestCase
$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);
}
}

View File

@@ -37,4 +37,11 @@ final class QuantizeColorsModifierTest extends ImagickTestCase
$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));
}
}