mirror of
https://github.com/Intervention/image.git
synced 2025-08-19 04:01:30 +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:
@@ -36,6 +36,10 @@ class PixelColorAnalyzer extends GenericPixelColorAnalyzer implements Specialize
|
|||||||
{
|
{
|
||||||
$index = @imagecolorat($gd, $this->x, $this->y);
|
$index = @imagecolorat($gd, $this->x, $this->y);
|
||||||
|
|
||||||
|
if (!imageistruecolor($gd)) {
|
||||||
|
$index = imagecolorsforindex($gd, $index);
|
||||||
|
}
|
||||||
|
|
||||||
if ($index === false) {
|
if ($index === false) {
|
||||||
throw new GeometryException(
|
throw new GeometryException(
|
||||||
'The specified position is not in the valid image area.'
|
'The specified position is not in the valid image area.'
|
||||||
|
@@ -25,6 +25,7 @@ class ColorProcessor implements ColorProcessorInterface
|
|||||||
*/
|
*/
|
||||||
public function __construct(protected ColorspaceInterface $colorspace = new Colorspace())
|
public function __construct(protected ColorspaceInterface $colorspace = new Colorspace())
|
||||||
{
|
{
|
||||||
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,14 +58,29 @@ class ColorProcessor implements ColorProcessorInterface
|
|||||||
*/
|
*/
|
||||||
public function nativeToColor(mixed $value): ColorInterface
|
public function nativeToColor(mixed $value): ColorInterface
|
||||||
{
|
{
|
||||||
if (!is_int($value)) {
|
if (!is_int($value) && !is_array($value)) {
|
||||||
throw new ColorException('GD driver can only decode colors in integer format.');
|
throw new ColorException('GD driver can only decode colors in integer and array format.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$a = ($value >> 24) & 0xFF;
|
if (is_array($value)) {
|
||||||
$r = ($value >> 16) & 0xFF;
|
// array conversion
|
||||||
$g = ($value >> 8) & 0xFF;
|
if (!$this->isValidArrayColor($value)) {
|
||||||
$b = $value & 0xFF;
|
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
|
// convert gd apha integer to intervention alpha integer
|
||||||
// ([opaque]0-127[transparent]) to ([opaque]255-0[transparent])
|
// ([opaque]0-127[transparent]) to ([opaque]255-0[transparent])
|
||||||
@@ -93,4 +109,49 @@ class ColorProcessor implements ColorProcessorInterface
|
|||||||
): float|int {
|
): float|int {
|
||||||
return ceil(((($input - $min) * ($targetMax - $targetMin)) / ($max - $min)) + $targetMin);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -26,7 +26,7 @@ final class ColorProcessorTest extends BaseTestCase
|
|||||||
$this->assertEquals(16725760, $result);
|
$this->assertEquals(16725760, $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNativeToColor(): void
|
public function testNativeToColorInteger(): void
|
||||||
{
|
{
|
||||||
$processor = new ColorProcessor();
|
$processor = new ColorProcessor();
|
||||||
$result = $processor->nativeToColor(16725760);
|
$result = $processor->nativeToColor(16725760);
|
||||||
@@ -37,6 +37,17 @@ final class ColorProcessorTest extends BaseTestCase
|
|||||||
$this->assertEquals(255, $result->channel(Alpha::class)->value());
|
$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
|
public function testNativeToColorInvalid(): void
|
||||||
{
|
{
|
||||||
$processor = new ColorProcessor();
|
$processor = new ColorProcessor();
|
||||||
|
@@ -55,4 +55,11 @@ final class QuantizeColorsModifierTest extends GdTestCase
|
|||||||
|
|
||||||
$this->assertEquals(count($colors), $count);
|
$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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -37,4 +37,11 @@ final class QuantizeColorsModifierTest extends ImagickTestCase
|
|||||||
$this->expectException(InputException::class);
|
$this->expectException(InputException::class);
|
||||||
$image->modify(new QuantizeColorsModifier(0));
|
$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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user