use GuzzleHttp\Psr7\Utils;
use Illuminate\Support\Facades\Cache;
use Intervention\Image\Decoders\BinaryImageDecoder;
+use Intervention\Image\Drivers\Gd\Decoders\NativeObjectDecoder;
use Intervention\Image\Drivers\Gd\Driver;
use Intervention\Image\Encoders\AutoEncoder;
use Intervention\Image\Encoders\PngEncoder;
use Intervention\Image\Interfaces\ImageInterface as InterventionImage;
use Intervention\Image\ImageManager;
+use Intervention\Image\Origin;
class ImageResizer
{
}
// If not in cache and thumbnail does not exist, generate thumb and cache path
- $thumbData = $this->resizeImageData($imageData, $width, $height, $keepRatio);
+ $thumbData = $this->resizeImageData($imageData, $width, $height, $keepRatio, $this->getExtension($image));
$disk->put($thumbFilePath, $thumbData, true);
Cache::put($thumbCacheKey, $thumbFilePath, static::THUMBNAIL_CACHE_TIME);
?string $format = null,
): string {
try {
- $thumb = $this->interventionFromImageData($imageData);
+ $thumb = $this->interventionFromImageData($imageData, $format);
} catch (Exception $e) {
throw new ImageUploadException(trans('errors.cannot_create_thumbs'));
}
* Performs some manual library usage to ensure image is specifically loaded
* from given binary data instead of data being misinterpreted.
*/
- protected function interventionFromImageData(string $imageData): InterventionImage
+ protected function interventionFromImageData(string $imageData, ?string $fileType): InterventionImage
{
$manager = new ImageManager(new Driver());
- return $manager->read($imageData, BinaryImageDecoder::class);
+ // Ensure gif images are decoded natively instead of deferring to intervention GIF
+ // handling since we don't need the added animation support.
+ $isGif = $fileType === 'gif';
+ $decoder = $isGif ? NativeObjectDecoder::class : BinaryImageDecoder::class;
+ $input = $isGif ? @imagecreatefromstring($imageData) : $imageData;
+
+ $image = $manager->read($input, $decoder);
+
+ if ($isGif) {
+ $image->setOrigin(new Origin('image/gif'));
+ }
+
+ return $image;
}
/**
*/
protected function isGif(Image $image): bool
{
- return strtolower(pathinfo($image->path, PATHINFO_EXTENSION)) === 'gif';
+ return $this->getExtension($image) === 'gif';
+ }
+
+ /**
+ * Get the extension for the given image, normalised to lower-case.
+ */
+ protected function getExtension(Image $image): string
+ {
+ return strtolower(pathinfo($image->path, PATHINFO_EXTENSION));
}
/**
$this->files->deleteAtRelativePath($relPath);
}
+ public function test_gif_thumbnail_generation()
+ {
+ $this->asAdmin();
+ $originalFile = $this->files->testFilePath('animated.gif');
+ $originalFileSize = filesize($originalFile);
+
+ $imgDetails = $this->files->uploadGalleryImageToPage($this, $this->entities->page(), 'animated.gif');
+ $relPath = $imgDetails['path'];
+
+ $this->assertTrue(file_exists(public_path($relPath)), 'Uploaded image found at path: ' . public_path($relPath));
+ $galleryThumb = $imgDetails['response']->thumbs->gallery;
+ $displayThumb = $imgDetails['response']->thumbs->display;
+
+ // Ensure display thumbnail is original image
+ $this->assertStringEndsWith($imgDetails['path'], $displayThumb);
+ $this->assertStringNotContainsString('thumbs', $displayThumb);
+
+ // Ensure gallery thumbnail is reduced image (single frame)
+ $galleryThumbRelPath = implode('/', array_slice(explode('/', $galleryThumb), 3));
+ $galleryThumbPath = public_path($galleryThumbRelPath);
+ $galleryFileSize = filesize($galleryThumbPath);
+
+ // Basic scan of GIF content to check frame count
+ $originalFrameCount = count(explode("\x00\x21\xF9", file_get_contents($originalFile)));
+ $galleryFrameCount = count(explode("\x00\x21\xF9", file_get_contents($galleryThumbPath)));
+
+ $this->files->deleteAtRelativePath($relPath);
+ $this->files->deleteAtRelativePath($galleryThumbRelPath);
+
+ $this->assertNotEquals($originalFileSize, $galleryFileSize);
+ $this->assertEquals(3, $originalFrameCount);
+ $this->assertEquals(1, $galleryFrameCount);
+ }
+
protected function getTestProfileImage()
{
$imageName = 'profile.png';