From 0f87254688e480fbb521e2a1ac6c11c784ca41af Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 1 Feb 2025 08:28:26 +0100 Subject: [PATCH] Fix bug in CropModifier of Imagick driver (#1428) The CropModifier produced strange artifacts if another resize step was performed after the modification. This patch removes the copying of the alpha channel in the CropModifier and implements a different method. See: https://github.com/Intervention/image/issues/1426 --- .../Imagick/Modifiers/CropModifier.php | 47 +++++++++++-------- tests/Feature/Imagick/CropResizePngTest.php | 19 ++++++++ 2 files changed, 46 insertions(+), 20 deletions(-) create mode 100644 tests/Feature/Imagick/CropResizePngTest.php diff --git a/src/Drivers/Imagick/Modifiers/CropModifier.php b/src/Drivers/Imagick/Modifiers/CropModifier.php index df5a30afa..6a2e8d31a 100644 --- a/src/Drivers/Imagick/Modifiers/CropModifier.php +++ b/src/Drivers/Imagick/Modifiers/CropModifier.php @@ -5,7 +5,7 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Imagick; -use Intervention\Image\Drivers\Imagick\Driver; +use ImagickPixel; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SpecializedInterface; use Intervention\Image\Modifiers\CropModifier as GenericCropModifier; @@ -14,20 +14,30 @@ class CropModifier extends GenericCropModifier implements SpecializedInterface { public function apply(ImageInterface $image): ImageInterface { - $crop = $this->crop($image); + // decode background color $background = $this->driver()->colorProcessor($image->colorspace())->colorToNative( $this->driver()->handleInput($this->background) ); // create empty container imagick to rebuild core $imagick = new Imagick(); + + // save resolution to add it later $resolution = $image->resolution()->perInch(); + // define position of the image on the new canvas + $crop = $this->crop($image); + $position = [ + ($crop->pivot()->x() + $this->offset_x) * -1, + ($crop->pivot()->y() + $this->offset_y) * -1, + ]; + foreach ($image as $frame) { // create new frame canvas with modifiers background $canvas = new Imagick(); $canvas->newImage($crop->width(), $crop->height(), $background, 'png'); $canvas->setImageResolution($resolution->x(), $resolution->y()); + $canvas->setImageAlphaChannel(Imagick::ALPHACHANNEL_SET); // or ALPHACHANNEL_ACTIVATE? // set animation details if ($image->isAnimated()) { @@ -36,31 +46,28 @@ public function apply(ImageInterface $image): ImageInterface $canvas->setImageDispose($frame->native()->getImageDispose()); } - // place original frame content onto the empty colored frame canvas - $canvas->compositeImage( - $frame->native(), - Imagick::COMPOSITE_DEFAULT, - ($crop->pivot()->x() + $this->offset_x) * -1, - ($crop->pivot()->y() + $this->offset_y) * -1, + // make the rectangular position of the original image transparent + // so that we can later place the original on top. this preserves + // the transparency of the original and shows the background color + // of the modifier in the other areas. if the original image has no + // transparent area the rectangular transparency will be covered by + // the original. + $clearer = new Imagick(); + $clearer->newImage( + $frame->native()->getImageWidth(), + $frame->native()->getImageHeight(), + new ImagickPixel('black'), ); + $canvas->compositeImage($clearer, Imagick::COMPOSITE_DSTOUT, ...$position); - // copy alpha channel if available - if ($frame->native()->getImageAlphaChannel()) { - $canvas->compositeImage( - $frame->native(), - version_compare(Driver::version(), '7.0.0', '>=') ? - Imagick::COMPOSITE_COPYOPACITY : - Imagick::COMPOSITE_DSTIN, - ($crop->pivot()->x() + $this->offset_x) * -1, - ($crop->pivot()->y() + $this->offset_y) * -1, - ); - } + // place original frame content onto prepared frame canvas + $canvas->compositeImage($frame->native(), Imagick::COMPOSITE_DEFAULT, ...$position); // add newly built frame to container imagick $imagick->addImage($canvas); } - // replace imagick + // replace imagick in the original image $image->core()->setNative($imagick); return $image; diff --git a/tests/Feature/Imagick/CropResizePngTest.php b/tests/Feature/Imagick/CropResizePngTest.php new file mode 100644 index 000000000..96f179bf4 --- /dev/null +++ b/tests/Feature/Imagick/CropResizePngTest.php @@ -0,0 +1,19 @@ +readTestImage('tile.png'); + $image->crop(100, 100); + $image->resize(200, 200); + $this->assertTransparency($image->pickColor(7, 22)); + $this->assertTransparency($image->pickColor(22, 7)); + } +}