From 3e7b8a97230da073137cd2ac025104b21a995995 Mon Sep 17 00:00:00 2001 From: Nicolas Lemoine Date: Wed, 3 Jun 2026 10:14:59 +0200 Subject: [PATCH] Preserve alpha channel in Imagick negative effect --- CHANGELOG.md | 1 + src/Imagick/Effects.php | 5 ++++- tests/tests/Effects/AbstractEffectsTest.php | 21 +++++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f056684dd..c973a24eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # CHANGELOG ### NEXT (YYYY-MM-DD) +- Fix the Imagick driver inverting the alpha channel in `effects()->negative()` (an opaque image became fully transparent); it now excludes the alpha channel, matching the GD driver ### 1.5.2 (2026-01-09) - Do not call curl_close on PHP 8.0+ (#875, @dmaicher) diff --git a/src/Imagick/Effects.php b/src/Imagick/Effects.php index a4d1e3ce5..1a1846eeb 100644 --- a/src/Imagick/Effects.php +++ b/src/Imagick/Effects.php @@ -75,7 +75,10 @@ public function gamma($correction) public function negative() { try { - $this->imagick->negateImage(false, \Imagick::CHANNEL_ALL); + // \Imagick::CHANNEL_ALL includes the alpha channel, so a plain negateImage() + // call would invert transparency along with the color (an opaque image would + // become fully transparent). Exclude the alpha channel to match the GD driver. + $this->imagick->negateImage(false, \Imagick::CHANNEL_ALL & ~\Imagick::CHANNEL_ALPHA); } catch (\ImagickException $e) { throw new RuntimeException('Failed to negate the image', $e->getCode(), $e); } diff --git a/tests/tests/Effects/AbstractEffectsTest.php b/tests/tests/Effects/AbstractEffectsTest.php index 2b9e3e061..d6a2f7c35 100644 --- a/tests/tests/Effects/AbstractEffectsTest.php +++ b/tests/tests/Effects/AbstractEffectsTest.php @@ -46,6 +46,27 @@ public function testNegate() $this->assertEquals('#ffff00', (string) $image->getColorAt(new Point(10, 10))); } + public function testNegatePreservesAlpha() + { + if (!$this->getDriverInfo()->hasFeature(Info::FEATURE_NEGATEIMAGE)) { + $this->isGoingToThrowException('Imagine\Exception\NotSupportedException'); + } + $palette = new RGB(); + $imagine = $this->getImagine(); + + // Negating must invert the color channels only, not the alpha channel: + // a fully-opaque image must remain fully opaque (see the Imagick driver, + // where CHANNEL_ALL used to invert the alpha and turn the image transparent). + $image = $imagine->create(new Box(20, 20), $palette->color('ff0')); + $image->effects() + ->negative(); + + $pixel = $image->getColorAt(new Point(10, 10)); + + $this->assertEquals('#0000ff', (string) $pixel); + $this->assertSame(100, $pixel->getAlpha()); + } + public function testGamma() { $palette = new RGB();