diff --git a/src/Imagick/Image.php b/src/Imagick/Image.php index 188e2d95..d3a5ef9a 100644 --- a/src/Imagick/Image.php +++ b/src/Imagick/Image.php @@ -125,6 +125,20 @@ public function getImagick() return $this->imagick; } + /** + * Replaces the underlying \Imagick instance, invalidating the cached + * layers (if any) since they wrap the replaced instance. + * + * @param \Imagick $imagick + */ + private function setImagick(\Imagick $imagick) + { + if ($imagick !== $this->imagick) { + $this->imagick = $imagick; + $this->layers = null; + } + } + /** * {@inheritdoc} * @@ -152,13 +166,13 @@ public function crop(PointInterface $start, BoxInterface $size) try { if ($this->layers()->count() > 1) { // Crop each layer separately - $this->imagick = $this->imagick->coalesceImages(); + $this->setImagick($this->imagick->coalesceImages()); foreach ($this->imagick as $frame) { $frame->cropImage($size->getWidth(), $size->getHeight(), $start->getX(), $start->getY()); // Reset canvas for gif format $frame->setImagePage(0, 0, 0, 0); } - $this->imagick = $this->imagick->deconstructImages(); + $this->setImagick($this->imagick->deconstructImages()); } else { $this->imagick->cropImage($size->getWidth(), $size->getHeight(), $start->getX(), $start->getY()); // Reset canvas for gif format @@ -284,11 +298,11 @@ public function resize(BoxInterface $size, $filter = ImageInterface::FILTER_UNDE { try { if ($this->layers()->count() > 1) { - $this->imagick = $this->imagick->coalesceImages(); + $this->setImagick($this->imagick->coalesceImages()); foreach ($this->imagick as $frame) { $frame->resizeImage($size->getWidth(), $size->getHeight(), $this->getFilter($filter), 1); } - $this->imagick = $this->imagick->deconstructImages(); + $this->setImagick($this->imagick->deconstructImages()); } else { $this->imagick->resizeImage($size->getWidth(), $size->getHeight(), $this->getFilter($filter), 1); } @@ -425,7 +439,7 @@ private function prepareOutput(array $options, $path = null) } else { $this->layers()->merge(); } - $this->imagick = $this->applyImageOptions($this->imagick, $options, $path); + $this->setImagick($this->applyImageOptions($this->imagick, $options, $path)); // flatten only if image has multiple layers if ((!isset($options['flatten']) || $options['flatten'] === true) && $this->layers()->count() > 1) { @@ -739,9 +753,9 @@ private function flatten() // @see https://github.com/mkoppanen/imagick/issues/45 try { if (method_exists($this->imagick, 'mergeImageLayers') && defined('Imagick::LAYERMETHOD_UNDEFINED')) { - $this->imagick = $this->imagick->mergeImageLayers(\Imagick::LAYERMETHOD_UNDEFINED); + $this->setImagick($this->imagick->mergeImageLayers(\Imagick::LAYERMETHOD_UNDEFINED)); } elseif (method_exists($this->imagick, 'flattenImages')) { - $this->imagick = $this->imagick->flattenImages(); + $this->setImagick($this->imagick->flattenImages()); } } catch (\ImagickException $e) { throw new RuntimeException('Flatten operation failed', $e->getCode(), $e); diff --git a/tests/tests/Imagick/ImageTest.php b/tests/tests/Imagick/ImageTest.php index fc2f08e4..53e914b0 100644 --- a/tests/tests/Imagick/ImageTest.php +++ b/tests/tests/Imagick/ImageTest.php @@ -143,6 +143,49 @@ public function testAnimatedGifCrop() ); } + public function testLayersReflectResizedFramesAfterResize() + { + $imagine = $this->getImagine(); + $image = $imagine->open(IMAGINE_TEST_FIXTURESFOLDER . '/anima.gif'); + $image->resize(new Box(59, 75)); + $layers = $image->layers(); + $this->assertCount(3, $layers); + $layers->coalesce(); + foreach ($layers as $index => $frame) { + $this->assertEquals(new Box(59, 75), $frame->getSize(), sprintf('Frame %d should have the resized size', $index)); + } + } + + public function testLayersReflectCroppedFramesAfterCrop() + { + $imagine = $this->getImagine(); + $image = $imagine->open(IMAGINE_TEST_FIXTURESFOLDER . '/anima.gif'); + // Crop inside the region where the animation frames actually differ, + // so that deconstructImages() keeps all the frames + $image->crop(new Point(110, 70), new Box(50, 40)); + $layers = $image->layers(); + $this->assertCount(3, $layers); + $layers->coalesce(); + foreach ($layers as $index => $frame) { + $this->assertEquals(new Box(50, 40), $frame->getSize(), sprintf('Frame %d should have the cropped size', $index)); + } + } + + public function testAnimatedDelayAndLoopsAreAppliedAfterResize() + { + $imagine = $this->getImagine(); + $image = $imagine->open(IMAGINE_TEST_FIXTURESFOLDER . '/anima.gif'); + $image->resize(new Box(59, 75)); + $filename = $this->getTemporaryFilename('.gif'); + $image->save($filename, array('animated' => true, 'animated.delay' => 500, 'animated.loops' => 3)); + $imagick = new \Imagick($filename); + $delay = $imagick->getImageDelay(); + $iterations = $imagick->getImageIterations(); + $imagick->clear(); + $this->assertSame(50, $delay); + $this->assertSame(3, $iterations); + } + public function testOptimize() { $imagine = $this->getImagine();