From 107e623e21d361cfa672160b6511bd142719e53f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 31 Jan 2025 17:14:11 +0100 Subject: [PATCH] Fix bug in imagick driver's CropModifier See: https://github.com/Intervention/image/issues/1426 --- .../Imagick/Modifiers/CropModifier.php | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/Drivers/Imagick/Modifiers/CropModifier.php b/src/Drivers/Imagick/Modifiers/CropModifier.php index df5a30af..89a91775 100644 --- a/src/Drivers/Imagick/Modifiers/CropModifier.php +++ b/src/Drivers/Imagick/Modifiers/CropModifier.php @@ -5,7 +5,6 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Imagick; -use Intervention\Image\Drivers\Imagick\Driver; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SpecializedInterface; use Intervention\Image\Modifiers\CropModifier as GenericCropModifier; @@ -14,20 +13,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 +45,23 @@ 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 entire rectangle at the 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 + $clear = new Imagick(); + $clear->newImage($frame->native()->getImageWidth(), $frame->native()->getImageHeight(), 'black'); + $canvas->compositeImage($clear, 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 the empty colored frame canvas + // with the transparent rectangle + $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;