From 2586b54965a40dd9af9dc2e4715913450172f7aa Mon Sep 17 00:00:00 2001 From: Guy Sartorelli Date: Thu, 11 Jul 2024 13:55:23 +1200 Subject: [PATCH] ENH Remove animation for thumbnails by default. --- code/Model/ThumbnailGenerator.php | 45 +++++++++++++++++++-- tests/php/Forms/fixtures/animated.gif | Bin 0 -> 9004 bytes tests/php/Model/ThumbnailGeneratorTest.php | 25 ++++++++++++ 3 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 tests/php/Forms/fixtures/animated.gif diff --git a/code/Model/ThumbnailGenerator.php b/code/Model/ThumbnailGenerator.php index 29dc6b2e9..87b5596f1 100644 --- a/code/Model/ThumbnailGenerator.php +++ b/code/Model/ThumbnailGenerator.php @@ -4,9 +4,11 @@ use LogicException; use SilverStripe\Assets\File; +use SilverStripe\Assets\Image_Backend; use SilverStripe\Assets\Storage\AssetContainer; use SilverStripe\Assets\Storage\AssetStore; use SilverStripe\Assets\Storage\DBFile; +use SilverStripe\Core\ClassInfo; use SilverStripe\Core\Config\Configurable; /** @@ -65,6 +67,8 @@ class ThumbnailGenerator */ private static $method = 'FitMax'; + private bool $allowsAnimation = false; + /** * Generate thumbnail and return the "src" property for this thumbnail * @@ -107,9 +111,33 @@ public function generateThumbnail(AssetContainer $file, $width, $height) $file = $file->existingOnly(); } - // Make large thumbnail - $method = $this->config()->get('method'); - return $file->$method($width, $height); + // Disable animation while generating thumbnail + $origAllowAnimation = null; + if (!$this->getAllowsAnimation()) { + if (ClassInfo::hasMethod($file, 'getImageBackend')) { + /** @var Image_Backend $backend */ + $backend = $file->getImageBackend(); + $origAllowAnimation = $backend->getAllowsAnimationInManipulations(); + $backend->setAllowsAnimationInManipulations(false); + } elseif ($file->getIsAnimated() && ClassInfo::hasMethod($file, 'RemoveAnimation')) { + $noAnimation = $file->RemoveAnimation(); + if ($noAnimation) { + $file = $noAnimation; + } + } + } + + try { + // Make large thumbnail + $method = $this->config()->get('method'); + $thumbnail = $file->$method($width, $height); + } finally { + if ($origAllowAnimation !== null) { + $backend->setAllowsAnimationInManipulations($origAllowAnimation); + } + } + + return $thumbnail; } /** @@ -173,4 +201,15 @@ public function setGenerates($generates) $this->generates = $generates; return $this; } + + public function getAllowsAnimation(): bool + { + return $this->allowsAnimation; + } + + public function setAllowsAnimation(bool $allows): static + { + $this->allowsAnimation = $allows; + return $this; + } } diff --git a/tests/php/Forms/fixtures/animated.gif b/tests/php/Forms/fixtures/animated.gif new file mode 100644 index 0000000000000000000000000000000000000000..e8963f17f7ff1daacd1dfdfe0c90b6630f5d4d41 GIT binary patch literal 9004 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, 100, 200); $this->assertEquals('/assets/906835357d/TestImage__FitMaxWzEwMCwyMDBd.png', $thumbnail); } + + public function provideAnimatedThumbnail(): array + { + return [ + [true], + [false], + ]; + } + + /** + * @dataProvider provideAnimatedThumbnail + */ + public function testAnimatedThumbnail(bool $allowAnimation): void + { + $image = new Image(); + $image->setFromLocalFile(__DIR__ . '/../Forms/fixtures/animated.gif', 'animated.gif'); + $image->write(); + + ThumbnailGenerator::config()->set('method', 'Fit'); + $generator = new ThumbnailGenerator(); + $generator->setAllowsAnimation($allowAnimation); + $thumbnail = $generator->generateThumbnail($image, 100, 100); + + $this->assertSame($allowAnimation, $thumbnail->getIsAnimated()); + } }