diff --git a/composer.json b/composer.json index e6d578c9..1d4f2061 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ ], "require": { "php": "^8.1", - "intervention/image": "^3.3", + "intervention/image": "^3.6", "league/flysystem": "^3.0", "psr/http-message": "^1.0|^2.0" }, diff --git a/docs/3.0/api/progressive-interlaced.md b/docs/3.0/api/progressive-interlaced.md new file mode 100644 index 00000000..e946a0fd --- /dev/null +++ b/docs/3.0/api/progressive-interlaced.md @@ -0,0 +1,26 @@ +--- +layout: default +title: Progressive & Interlaced +--- + +## Progressive & Interlaced Images + +## Interlace `interlace` + +The `interlace` parameter controls whether an image is rendered in a progressive or interlaced format. This feature enhances the loading experience of images, making them appear gradually as they are downloaded, which can improve the user experience on slower connections. + +> Caution: For GIF/PNG, it can generate a slightly larger file size. + +### Supported Formats + +- **JPG**: The `onterlace` parameter applies a progressive scan to JPG images. +- **PNG** and **GIF**: The `interlace` parameter enables interlacing for GIF/PNG images. + +> Note: When `ext` is set to `.pjpg`, it will automatically generate a progressive JPG image, regardless of the `interlace` parameter. + +~~~ html + + +~~~ + +[![© Photo Joel Reynolds](https://glide.herokuapp.com/1.0/kayaks.jpg?interlace=1)](https://glide.herokuapp.com/1.0/kayaks.jpg?interlace=1) diff --git a/docs/3.0/config/setup.md b/docs/3.0/config/setup.md index f52e2129..e94728ad 100644 --- a/docs/3.0/config/setup.md +++ b/docs/3.0/config/setup.md @@ -77,7 +77,6 @@ $manipulators = [ new League\Glide\Manipulators\Watermark($watermarks), new League\Glide\Manipulators\Background(), new League\Glide\Manipulators\Border(), - new League\Glide\Manipulators\Encode(), ]; // Set API diff --git a/docs/_data/menu.yml b/docs/_data/menu.yml index 5aced23a..9a7065c2 100644 --- a/docs/_data/menu.yml +++ b/docs/_data/menu.yml @@ -25,6 +25,7 @@ Background: '/3.0/api/background/' Border: '/3.0/api/border/' Encode: '/3.0/api/encode/' + Progressive & Interlaced: '/3.0/api/progressive-interlaced/' '2.0': Getting Started: Introduction: '/' diff --git a/src/Api/Api.php b/src/Api/Api.php index 42799a29..45be6f99 100644 --- a/src/Api/Api.php +++ b/src/Api/Api.php @@ -3,6 +3,7 @@ namespace League\Glide\Api; use Intervention\Image\ImageManager; +use Intervention\Image\Interfaces\ImageInterface; use League\Glide\Manipulators\ManipulatorInterface; class Api implements ApiInterface @@ -91,10 +92,25 @@ public function run(string $source, array $params): string foreach ($this->manipulators as $manipulator) { $manipulator->setParams($params); - $image = $manipulator->run($image); } - return $image->encodeByMediaType()->toString(); + return $this->encode($image, $params); + } + + /** + * Perform image encoding to a given format. + * + * @param ImageInterface $image Image object + * @param array $params the manipulator params + * + * @return string Manipulated image binary data + */ + public function encode(ImageInterface $image, array $params): string + { + $encoder = new Encoder($params); + $encoded = $encoder->run($image); + + return $encoded->toString(); } } diff --git a/src/Api/Encoder.php b/src/Api/Encoder.php new file mode 100644 index 00000000..bef473b2 --- /dev/null +++ b/src/Api/Encoder.php @@ -0,0 +1,151 @@ +params = $params; + } + + /** + * Set the manipulation params. + * + * @param array $params The manipulation params. + * + * @return $this + */ + public function setParams(array $params) + { + $this->params = $params; + + return $this; + } + + /** + * Get a specific manipulation param. + */ + public function getParam(string $name): mixed + { + return array_key_exists($name, $this->params) + ? $this->params[$name] + : null; + } + + /** + * Perform output image manipulation. + * + * @param ImageInterface $image The source image. + * + * @return EncodedImageInterface The encoded image. + */ + public function run(ImageInterface $image): EncodedImageInterface + { + $format = $this->getFormat($image); + $quality = $this->getQuality(); + $shouldInterlace = filter_var($this->getParam('interlace'), FILTER_VALIDATE_BOOLEAN); + + if ('pjpg' === $format) { + $shouldInterlace = true; + $format = 'jpg'; + } + + $encoderOptions = ['extension' => $format]; + switch ($format) { + case 'avif': + case 'heic': + case 'tiff': + case 'webp': + $encoderOptions['quality'] = $quality; + break; + case 'jpg': + $encoderOptions['quality'] = $quality; + $encoderOptions['progressive'] = $shouldInterlace; + break; + case 'gif': + case 'png': + $encoderOptions['interlaced'] = $shouldInterlace; + break; + default: + throw new \Exception("Invalid format provided: {$format}"); + } + + return $image->encodeByExtension(...$encoderOptions); + } + + /** + * Resolve format. + * + * @param ImageInterface $image The source image. + * + * @return string The resolved format. + */ + public function getFormat(ImageInterface $image): string + { + $fm = (string) $this->getParam('fm'); + + if ($fm && array_key_exists($fm, static::supportedFormats())) { + return $fm; + } + + /** @psalm-suppress RiskyTruthyFalsyComparison */ + return array_search($image->origin()->mediaType(), static::supportedFormats(), true) ?: 'jpg'; + } + + /** + * Get a list of supported image formats and MIME types. + * + * @return array + */ + public static function supportedFormats(): array + { + return [ + 'avif' => 'image/avif', + 'gif' => 'image/gif', + 'jpg' => 'image/jpeg', + 'pjpg' => 'image/jpeg', + 'png' => 'image/png', + 'webp' => 'image/webp', + 'tiff' => 'image/tiff', + 'heic' => 'image/heic', + ]; + } + + /** + * Resolve quality. + * + * @return int The resolved quality. + */ + public function getQuality(): int + { + $default = 90; + $q = $this->getParam('q'); + + if ( + !is_numeric($q) + || $q < 0 + || $q > 100 + ) { + return $default; + } + + return (int) $q; + } +} diff --git a/src/Manipulators/Encode.php b/src/Manipulators/Encode.php deleted file mode 100644 index 1ce47de2..00000000 --- a/src/Manipulators/Encode.php +++ /dev/null @@ -1,114 +0,0 @@ -getFormat($image); - $quality = $this->getQuality(); - $driver = $image->driver(); - $interlace = false; - - if ('pjpg' === $format) { - $interlace = true; - - $format = 'jpg'; - } - - $image = (new ImageManager($driver))->read( - $image->encodeByExtension($format, quality: $quality)->toString() - ); - - if ($interlace) { - $image = $this->interlace($image, $driver); - } - - return $image; - } - - /** - * Resolve format. - * - * @param ImageInterface $image The source image. - * - * @return string The resolved format. - */ - public function getFormat(ImageInterface $image): string - { - $fm = (string) $this->getParam('fm'); - - if ($fm && array_key_exists($fm, static::supportedFormats())) { - return $fm; - } - - /** @psalm-suppress RiskyTruthyFalsyComparison */ - return array_search($image->origin()->mediaType(), static::supportedFormats(), true) ?: 'jpg'; - } - - /** - * Get a list of supported image formats and MIME types. - * - * @return array - */ - public static function supportedFormats(): array - { - return [ - 'avif' => 'image/avif', - 'gif' => 'image/gif', - 'jpg' => 'image/jpeg', - 'pjpg' => 'image/jpeg', - 'png' => 'image/png', - 'webp' => 'image/webp', - 'tiff' => 'image/tiff', - 'heic' => 'image/heic', - ]; - } - - /** - * Resolve quality. - * - * @return int The resolved quality. - */ - public function getQuality(): int - { - $default = 90; - $q = $this->getParam('q'); - - if (!is_numeric($q) - || $q < 0 - || $q > 100 - ) { - return $default; - } - - return (int) $q; - } - - protected function interlace(ImageInterface $image, DriverInterface $driver): ImageInterface - { - $img = $image->core()->native(); - - if ($driver instanceof ImagickDriver) { - $img->setInterlaceScheme(\Imagick::INTERLACE_PLANE); - } elseif ($driver instanceof GdDriver) { - imageinterlace($img, true); - } - - return $image; - } -} diff --git a/src/ServerFactory.php b/src/ServerFactory.php index eb08e7b4..d2e9acc5 100644 --- a/src/ServerFactory.php +++ b/src/ServerFactory.php @@ -13,7 +13,6 @@ use League\Glide\Manipulators\Brightness; use League\Glide\Manipulators\Contrast; use League\Glide\Manipulators\Crop; -use League\Glide\Manipulators\Encode; use League\Glide\Manipulators\Filter; use League\Glide\Manipulators\Flip; use League\Glide\Manipulators\Gamma; @@ -255,7 +254,6 @@ public function getManipulators(): array new Watermark($this->getWatermarks(), $this->getWatermarksPathPrefix() ?? ''), new Background(), new Border(), - new Encode(), ]; } diff --git a/tests/Api/ApiTest.php b/tests/Api/ApiTest.php index da3b58cc..c55b2c15 100644 --- a/tests/Api/ApiTest.php +++ b/tests/Api/ApiTest.php @@ -61,7 +61,11 @@ public function testGetManipulators() public function testRun() { $image = \Mockery::mock(ImageInterface::class, function ($mock) { - $mock->shouldReceive('encodeByMediaType')->andReturn(\Mockery::mock(EncodedImageInterface::class, function ($mock) { + $mock->shouldReceive('origin')->andReturn(\Mockery::mock('\Intervention\Image\Origin', function ($mock) { + $mock->shouldReceive('mediaType')->andReturn('image/png'); + })); + + $mock->shouldReceive('encodeByExtension')->with('png')->andReturn(\Mockery::mock(EncodedImageInterface::class, function ($mock) { $mock->shouldReceive('toString')->andReturn('encoded'); })); }); diff --git a/tests/Api/EncoderTest.php b/tests/Api/EncoderTest.php new file mode 100644 index 00000000..b3bae46c --- /dev/null +++ b/tests/Api/EncoderTest.php @@ -0,0 +1,224 @@ +jpg = $manager->read( + $manager->create(100, 100)->encode(new MediaTypeEncoder('image/jpeg'))->toFilePointer() + ); + $this->png = $manager->read( + $manager->create(100, 100)->encode(new MediaTypeEncoder('image/png'))->toFilePointer() + ); + $this->gif = $manager->read( + $manager->create(100, 100)->encode(new MediaTypeEncoder('image/gif'))->toFilePointer() + ); + + if (function_exists('imagecreatefromwebp')) { + $this->webp = $manager->read( + $manager->create(100, 100)->encode(new MediaTypeEncoder('image/webp'))->toFilePointer() + ); + } + + if (function_exists('imagecreatefromavif')) { + $this->avif = $manager->read( + $manager->create(100, 100)->encode(new MediaTypeEncoder('image/avif'))->toFilePointer() + ); + } + + $this->encoder = new Encoder(); + } + + public function tearDown(): void + { + \Mockery::close(); + } + + public function testCreateInstance(): void + { + /** + * @psalm-suppress ArgumentTypeCoercion + */ + $this->assertInstanceOf(Encoder::class, $this->encoder); + } + + public function testRun(): void + { + $this->assertSame('image/jpeg', $this->getMime($this->encoder->setParams(['fm' => 'jpg'])->run($this->jpg))); + $this->assertSame('image/jpeg', $this->getMime($this->encoder->setParams(['fm' => 'jpg'])->run($this->png))); + $this->assertSame('image/jpeg', $this->getMime($this->encoder->setParams(['fm' => 'jpg'])->run($this->gif))); + $this->assertSame('image/jpeg', $this->getMime($this->encoder->setParams(['fm' => 'pjpg'])->run($this->jpg))); + $this->assertSame('image/jpeg', $this->getMime($this->encoder->setParams(['fm' => 'pjpg'])->run($this->png))); + $this->assertSame('image/jpeg', $this->getMime($this->encoder->setParams(['fm' => 'pjpg'])->run($this->gif))); + $this->assertSame('image/png', $this->getMime($this->encoder->setParams(['fm' => 'png'])->run($this->jpg))); + $this->assertSame('image/png', $this->getMime($this->encoder->setParams(['fm' => 'png'])->run($this->png))); + $this->assertSame('image/png', $this->getMime($this->encoder->setParams(['fm' => 'png'])->run($this->gif))); + $this->assertSame('image/gif', $this->getMime($this->encoder->setParams(['fm' => 'gif'])->run($this->jpg))); + $this->assertSame('image/gif', $this->getMime($this->encoder->setParams(['fm' => 'gif'])->run($this->png))); + $this->assertSame('image/gif', $this->getMime($this->encoder->setParams(['fm' => 'gif'])->run($this->gif))); + + if (function_exists('imagecreatefromwebp')) { + $this->assertSame('image/jpeg', $this->getMime($this->encoder->setParams(['fm' => 'jpg'])->run($this->webp))); + $this->assertSame('image/jpeg', $this->getMime($this->encoder->setParams(['fm' => 'pjpg'])->run($this->webp))); + $this->assertSame('image/png', $this->getMime($this->encoder->setParams(['fm' => 'png'])->run($this->webp))); + $this->assertSame('image/gif', $this->getMime($this->encoder->setParams(['fm' => 'gif'])->run($this->webp))); + $this->assertSame('image/webp', $this->getMime($this->encoder->setParams(['fm' => 'webp'])->run($this->jpg))); + $this->assertSame('image/webp', $this->getMime($this->encoder->setParams(['fm' => 'webp'])->run($this->png))); + $this->assertSame('image/webp', $this->getMime($this->encoder->setParams(['fm' => 'webp'])->run($this->gif))); + $this->assertSame('image/webp', $this->getMime($this->encoder->setParams(['fm' => 'webp'])->run($this->webp))); + } + if (function_exists('imagecreatefromavif')) { + $this->assertSame('image/jpeg', $this->getMime($this->encoder->setParams(['fm' => 'jpg'])->run($this->avif))); + $this->assertSame('image/jpeg', $this->getMime($this->encoder->setParams(['fm' => 'pjpg'])->run($this->avif))); + $this->assertSame('image/png', $this->getMime($this->encoder->setParams(['fm' => 'png'])->run($this->avif))); + $this->assertSame('image/gif', $this->getMime($this->encoder->setParams(['fm' => 'gif'])->run($this->avif))); + $this->assertSame('image/avif', $this->getMime($this->encoder->setParams(['fm' => 'avif'])->run($this->jpg))); + $this->assertSame('image/avif', $this->getMime($this->encoder->setParams(['fm' => 'avif'])->run($this->png))); + $this->assertSame('image/avif', $this->getMime($this->encoder->setParams(['fm' => 'avif'])->run($this->gif))); + $this->assertSame('image/avif', $this->getMime($this->encoder->setParams(['fm' => 'avif'])->run($this->avif))); + } + + if (function_exists('imagecreatefromwebp') && function_exists('imagecreatefromavif')) { + $this->assertSame('image/webp', $this->getMime($this->encoder->setParams(['fm' => 'webp'])->run($this->avif))); + $this->assertSame('image/avif', $this->getMime($this->encoder->setParams(['fm' => 'avif'])->run($this->webp))); + } + } + + public function testGetFormat(): void + { + /** + * @psalm-suppress MissingClosureParamType + */ + $image = \Mockery::mock(ImageInterface::class, function ($mock) { + /* + * @var Mock $mock + */ + $this->assertMediaType($mock, 'image/jpeg')->once(); + $this->assertMediaType($mock, 'image/png')->once(); + $this->assertMediaType($mock, 'image/gif')->once(); + $this->assertMediaType($mock, 'image/bmp')->once(); + $this->assertMediaType($mock, 'image/jpeg')->twice(); + + if (function_exists('imagecreatefromwebp')) { + $this->assertMediaType($mock, 'image/webp')->once(); + } + + if (function_exists('imagecreatefromavif')) { + $this->assertMediaType($mock, 'image/avif')->once(); + } + }); + + $this->assertSame('jpg', $this->encoder->setParams(['fm' => 'jpg'])->getFormat($image)); + $this->assertSame('png', $this->encoder->setParams(['fm' => 'png'])->getFormat($image)); + $this->assertSame('gif', $this->encoder->setParams(['fm' => 'gif'])->getFormat($image)); + $this->assertSame('jpg', $this->encoder->setParams(['fm' => null])->getFormat($image)); + $this->assertSame('png', $this->encoder->setParams(['fm' => null])->getFormat($image)); + $this->assertSame('gif', $this->encoder->setParams(['fm' => null])->getFormat($image)); + $this->assertSame('jpg', $this->encoder->setParams(['fm' => null])->getFormat($image)); + + $this->assertSame('jpg', $this->encoder->setParams(['fm' => ''])->getFormat($image)); + $this->assertSame('jpg', $this->encoder->setParams(['fm' => 'invalid'])->getFormat($image)); + + if (function_exists('imagecreatefromwebp')) { + $this->assertSame('webp', $this->encoder->setParams(['fm' => null])->getFormat($image)); + } + + if (function_exists('imagecreatefromavif')) { + $this->assertSame('avif', $this->encoder->setParams(['fm' => null])->getFormat($image)); + } + } + + public function testGetQuality(): void + { + $this->assertSame(100, $this->encoder->setParams(['q' => '100'])->getQuality()); + $this->assertSame(100, $this->encoder->setParams(['q' => 100])->getQuality()); + $this->assertSame(90, $this->encoder->setParams(['q' => null])->getQuality()); + $this->assertSame(90, $this->encoder->setParams(['q' => 'a'])->getQuality()); + $this->assertSame(50, $this->encoder->setParams(['q' => '50.50'])->getQuality()); + $this->assertSame(90, $this->encoder->setParams(['q' => '-1'])->getQuality()); + $this->assertSame(90, $this->encoder->setParams(['q' => '101'])->getQuality()); + } + + /** + * Test functions that require the imagick extension. + */ + public function testWithImagick(): void + { + if (!extension_loaded('imagick')) { + $this->markTestSkipped( + 'The imagick extension is not available.' + ); + } + $manager = ImageManager::imagick(); + // These need to be recreated with the imagick driver selected in the manager + $this->jpg = $manager->read($manager->create(100, 100)->encode(new MediaTypeEncoder('image/jpeg'))->toFilePointer()); + $this->png = $manager->read($manager->create(100, 100)->encode(new MediaTypeEncoder('image/png'))->toFilePointer()); + $this->gif = $manager->read($manager->create(100, 100)->encode(new MediaTypeEncoder('image/gif'))->toFilePointer()); + $this->heic = $manager->read($manager->create(100, 100)->encode(new MediaTypeEncoder('image/heic'))->toFilePointer()); + $this->tif = $manager->read($manager->create(100, 100)->encode(new MediaTypeEncoder('image/tiff'))->toFilePointer()); + + $this->assertSame('image/tiff', $this->getMime($this->encoder->setParams(['fm' => 'tiff'])->run($this->jpg))); + $this->assertSame('image/tiff', $this->getMime($this->encoder->setParams(['fm' => 'tiff'])->run($this->png))); + $this->assertSame('image/tiff', $this->getMime($this->encoder->setParams(['fm' => 'tiff'])->run($this->gif))); + $this->assertSame('image/tiff', $this->getMime($this->encoder->setParams(['fm' => 'tiff'])->run($this->heic))); + } + + public function testSupportedFormats(): void + { + $expected = [ + 'avif' => 'image/avif', + 'gif' => 'image/gif', + 'jpg' => 'image/jpeg', + 'pjpg' => 'image/jpeg', + 'png' => 'image/png', + 'webp' => 'image/webp', + 'tiff' => 'image/tiff', + 'heic' => 'image/heic', + ]; + + $this->assertSame($expected, Encoder::supportedFormats()); + } + + protected function getMime(EncodedImageInterface $image): string + { + return $image->mediaType(); + } + + /** + * Creates an assertion to check media type. + * + * @param Mock $mock + * @param string $mediaType + * + * @psalm-suppress MoreSpecificReturnType + */ + protected function assertMediaType($mock, $mediaType): Mockery\CompositeExpectation + { + /* + * @var Mock $mock + */ + /** + * @psalm-suppress LessSpecificReturnStatement, UndefinedMagicMethod + */ + return $mock->shouldReceive('origin')->andReturn(\Mockery::mock('Intervention\Image\Origin', ['mediaType' => $mediaType])); + } +} diff --git a/tests/Manipulators/EncodeTest.php b/tests/Manipulators/EncodeTest.php deleted file mode 100644 index f3a5d1d6..00000000 --- a/tests/Manipulators/EncodeTest.php +++ /dev/null @@ -1,195 +0,0 @@ -jpg = $manager->read( - $manager->create(100, 100)->encode(new MediaTypeEncoder('image/jpeg'))->toFilePointer() - ); - $this->png = $manager->read( - $manager->create(100, 100)->encode(new MediaTypeEncoder('image/png'))->toFilePointer() - ); - $this->gif = $manager->read( - $manager->create(100, 100)->encode(new MediaTypeEncoder('image/gif'))->toFilePointer() - ); - - if (function_exists('imagecreatefromwebp')) { - $this->webp = $manager->read( - $manager->create(100, 100)->encode(new MediaTypeEncoder('image/webp'))->toFilePointer() - ); - } - - if (function_exists('imagecreatefromavif')) { - $this->avif = $manager->read( - $manager->create(100, 100)->encode(new MediaTypeEncoder('image/avif'))->toFilePointer() - ); - } - - $this->manipulator = new Encode(); - } - - public function tearDown(): void - { - \Mockery::close(); - } - - public function testCreateInstance() - { - $this->assertInstanceOf('League\Glide\Manipulators\Encode', $this->manipulator); - } - - public function testRun() - { - $this->assertSame('image/jpeg', $this->getMime($this->manipulator->setParams(['fm' => 'jpg'])->run($this->jpg))); - $this->assertSame('image/jpeg', $this->getMime($this->manipulator->setParams(['fm' => 'jpg'])->run($this->png))); - $this->assertSame('image/jpeg', $this->getMime($this->manipulator->setParams(['fm' => 'jpg'])->run($this->gif))); - $this->assertSame('image/jpeg', $this->getMime($this->manipulator->setParams(['fm' => 'pjpg'])->run($this->jpg))); - $this->assertSame('image/jpeg', $this->getMime($this->manipulator->setParams(['fm' => 'pjpg'])->run($this->png))); - $this->assertSame('image/jpeg', $this->getMime($this->manipulator->setParams(['fm' => 'pjpg'])->run($this->gif))); - $this->assertSame('image/png', $this->getMime($this->manipulator->setParams(['fm' => 'png'])->run($this->jpg))); - $this->assertSame('image/png', $this->getMime($this->manipulator->setParams(['fm' => 'png'])->run($this->png))); - $this->assertSame('image/png', $this->getMime($this->manipulator->setParams(['fm' => 'png'])->run($this->gif))); - $this->assertSame('image/gif', $this->getMime($this->manipulator->setParams(['fm' => 'gif'])->run($this->jpg))); - $this->assertSame('image/gif', $this->getMime($this->manipulator->setParams(['fm' => 'gif'])->run($this->png))); - $this->assertSame('image/gif', $this->getMime($this->manipulator->setParams(['fm' => 'gif'])->run($this->gif))); - - if (function_exists('imagecreatefromwebp')) { - $this->assertSame('image/jpeg', $this->getMime($this->manipulator->setParams(['fm' => 'jpg'])->run($this->webp))); - $this->assertSame('image/jpeg', $this->getMime($this->manipulator->setParams(['fm' => 'pjpg'])->run($this->webp))); - $this->assertSame('image/png', $this->getMime($this->manipulator->setParams(['fm' => 'png'])->run($this->webp))); - $this->assertSame('image/gif', $this->getMime($this->manipulator->setParams(['fm' => 'gif'])->run($this->webp))); - $this->assertSame('image/webp', $this->getMime($this->manipulator->setParams(['fm' => 'webp'])->run($this->jpg))); - $this->assertSame('image/webp', $this->getMime($this->manipulator->setParams(['fm' => 'webp'])->run($this->png))); - $this->assertSame('image/webp', $this->getMime($this->manipulator->setParams(['fm' => 'webp'])->run($this->gif))); - $this->assertSame('image/webp', $this->getMime($this->manipulator->setParams(['fm' => 'webp'])->run($this->webp))); - } - if (function_exists('imagecreatefromavif')) { - $this->assertSame('image/jpeg', $this->getMime($this->manipulator->setParams(['fm' => 'jpg'])->run($this->avif))); - $this->assertSame('image/jpeg', $this->getMime($this->manipulator->setParams(['fm' => 'pjpg'])->run($this->avif))); - $this->assertSame('image/png', $this->getMime($this->manipulator->setParams(['fm' => 'png'])->run($this->avif))); - $this->assertSame('image/gif', $this->getMime($this->manipulator->setParams(['fm' => 'gif'])->run($this->avif))); - $this->assertSame('image/avif', $this->getMime($this->manipulator->setParams(['fm' => 'avif'])->run($this->jpg))); - $this->assertSame('image/avif', $this->getMime($this->manipulator->setParams(['fm' => 'avif'])->run($this->png))); - $this->assertSame('image/avif', $this->getMime($this->manipulator->setParams(['fm' => 'avif'])->run($this->gif))); - $this->assertSame('image/avif', $this->getMime($this->manipulator->setParams(['fm' => 'avif'])->run($this->avif))); - } - - if (function_exists('imagecreatefromwebp') && function_exists('imagecreatefromavif')) { - $this->assertSame('image/webp', $this->getMime($this->manipulator->setParams(['fm' => 'webp'])->run($this->avif))); - $this->assertSame('image/avif', $this->getMime($this->manipulator->setParams(['fm' => 'avif'])->run($this->webp))); - } - } - - public function testGetFormat() - { - $image = \Mockery::mock(ImageInterface::class, function ($mock) { - $mock->shouldReceive('origin')->andReturn(\Mockery::mock('Intervention\Image\Origin', ['mediaType' => 'image/jpeg']))->once(); - $mock->shouldReceive('origin')->andReturn(\Mockery::mock('Intervention\Image\Origin', ['mediaType' => 'image/png']))->once(); - $mock->shouldReceive('origin')->andReturn(\Mockery::mock('Intervention\Image\Origin', ['mediaType' => 'image/gif']))->once(); - $mock->shouldReceive('origin')->andReturn(\Mockery::mock('Intervention\Image\Origin', ['mediaType' => 'image/bmp']))->once(); - $mock->shouldReceive('origin')->andReturn(\Mockery::mock('Intervention\Image\Origin', ['mediaType' => 'image/jpeg']))->twice(); - - if (function_exists('imagecreatefromwebp')) { - $mock->shouldReceive('origin')->andReturn(\Mockery::mock('Intervention\Image\Origin', ['mediaType' => 'image/webp']))->once(); - } - - if (function_exists('imagecreatefromavif')) { - $mock->shouldReceive('origin')->andReturn(\Mockery::mock('Intervention\Image\Origin', ['mediaType' => 'image/avif']))->once(); - } - }); - - $this->assertSame('jpg', $this->manipulator->setParams(['fm' => 'jpg'])->getFormat($image)); - $this->assertSame('png', $this->manipulator->setParams(['fm' => 'png'])->getFormat($image)); - $this->assertSame('gif', $this->manipulator->setParams(['fm' => 'gif'])->getFormat($image)); - $this->assertSame('jpg', $this->manipulator->setParams(['fm' => null])->getFormat($image)); - $this->assertSame('png', $this->manipulator->setParams(['fm' => null])->getFormat($image)); - $this->assertSame('gif', $this->manipulator->setParams(['fm' => null])->getFormat($image)); - $this->assertSame('jpg', $this->manipulator->setParams(['fm' => null])->getFormat($image)); - $this->assertSame('jpg', $this->manipulator->setParams(['fm' => ''])->getFormat($image)); - $this->assertSame('jpg', $this->manipulator->setParams(['fm' => 'invalid'])->getFormat($image)); - - if (function_exists('imagecreatefromwebp')) { - $this->assertSame('webp', $this->manipulator->setParams(['fm' => null])->getFormat($image)); - } - - if (function_exists('imagecreatefromavif')) { - $this->assertSame('avif', $this->manipulator->setParams(['fm' => null])->getFormat($image)); - } - } - - public function testGetQuality() - { - $this->assertSame(100, $this->manipulator->setParams(['q' => '100'])->getQuality()); - $this->assertSame(100, $this->manipulator->setParams(['q' => 100])->getQuality()); - $this->assertSame(90, $this->manipulator->setParams(['q' => null])->getQuality()); - $this->assertSame(90, $this->manipulator->setParams(['q' => 'a'])->getQuality()); - $this->assertSame(50, $this->manipulator->setParams(['q' => '50.50'])->getQuality()); - $this->assertSame(90, $this->manipulator->setParams(['q' => '-1'])->getQuality()); - $this->assertSame(90, $this->manipulator->setParams(['q' => '101'])->getQuality()); - } - - /** - * Test functions that require the imagick extension. - * - * @return void - */ - public function testWithImagick() - { - if (!extension_loaded('imagick')) { - $this->markTestSkipped( - 'The imagick extension is not available.' - ); - } - $manager = ImageManager::imagick(); - // These need to be recreated with the imagick driver selected in the manager - $this->jpg = $manager->read($manager->create(100, 100)->encode(new MediaTypeEncoder('image/jpeg'))->toFilePointer()); - $this->png = $manager->read($manager->create(100, 100)->encode(new MediaTypeEncoder('image/png'))->toFilePointer()); - $this->gif = $manager->read($manager->create(100, 100)->encode(new MediaTypeEncoder('image/gif'))->toFilePointer()); - $this->heic = $manager->read($manager->create(100, 100)->encode(new MediaTypeEncoder('image/heic'))->toFilePointer()); - $this->tif = $manager->read($manager->create(100, 100)->encode(new MediaTypeEncoder('image/tiff'))->toFilePointer()); - - $this->assertSame('image/tiff', $this->getMime($this->manipulator->setParams(['fm' => 'tiff'])->run($this->jpg))); - $this->assertSame('image/tiff', $this->getMime($this->manipulator->setParams(['fm' => 'tiff'])->run($this->png))); - $this->assertSame('image/tiff', $this->getMime($this->manipulator->setParams(['fm' => 'tiff'])->run($this->gif))); - $this->assertSame('image/tiff', $this->getMime($this->manipulator->setParams(['fm' => 'tiff'])->run($this->heic))); - } - - public function testSupportedFormats() - { - $expected = [ - 'avif' => 'image/avif', - 'gif' => 'image/gif', - 'jpg' => 'image/jpeg', - 'pjpg' => 'image/jpeg', - 'png' => 'image/png', - 'webp' => 'image/webp', - 'tiff' => 'image/tiff', - 'heic' => 'image/heic', - ]; - - $this->assertSame($expected, Encode::supportedFormats()); - } - - protected function getMime(ImageInterface $image) - { - return $image->origin()->mediaType(); - } -}