mirror of
https://github.com/Intervention/image.git
synced 2025-08-06 13:56:30 +02:00
Add Encoder Options for progressive Jpeg & interlaced GIF format
This commit is contained in:
@@ -21,7 +21,7 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php": "^8.1",
|
"php": "^8.1",
|
||||||
"ext-mbstring": "*",
|
"ext-mbstring": "*",
|
||||||
"intervention/gif": "^4.0.1"
|
"intervention/gif": "^4.1"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^10.0",
|
"phpunit/phpunit": "^10.0",
|
||||||
|
@@ -23,7 +23,9 @@ class GifEncoder extends GenericGifEncoder implements SpecializedInterface
|
|||||||
|
|
||||||
$gd = $image->core()->native();
|
$gd = $image->core()->native();
|
||||||
$data = $this->buffered(function () use ($gd) {
|
$data = $this->buffered(function () use ($gd) {
|
||||||
|
imageinterlace($gd, $this->interlaced);
|
||||||
imagegif($gd);
|
imagegif($gd);
|
||||||
|
imageinterlace($gd, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
return new EncodedImage($data, 'image/gif');
|
return new EncodedImage($data, 'image/gif');
|
||||||
@@ -41,8 +43,9 @@ class GifEncoder extends GenericGifEncoder implements SpecializedInterface
|
|||||||
|
|
||||||
foreach ($image as $frame) {
|
foreach ($image as $frame) {
|
||||||
$builder->addFrame(
|
$builder->addFrame(
|
||||||
(string) $this->encode($frame->toImage($image->driver())),
|
source: (string) $this->encode($frame->toImage($image->driver())),
|
||||||
$frame->delay()
|
delay: $frame->delay(),
|
||||||
|
interlaced: $this->interlaced
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -17,6 +17,7 @@ class JpegEncoder extends GenericJpegEncoder implements SpecializedInterface
|
|||||||
$output = Cloner::cloneBlended($image->core()->native(), background: $image->blendingColor());
|
$output = Cloner::cloneBlended($image->core()->native(), background: $image->blendingColor());
|
||||||
|
|
||||||
$data = $this->buffered(function () use ($output) {
|
$data = $this->buffered(function () use ($output) {
|
||||||
|
imageinterlace($output, $this->progressive);
|
||||||
imagejpeg($output, null, $this->quality);
|
imagejpeg($output, null, $this->quality);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -24,6 +24,10 @@ class GifEncoder extends GenericGifEncoder implements SpecializedInterface
|
|||||||
$imagick->setCompression($compression);
|
$imagick->setCompression($compression);
|
||||||
$imagick->setImageCompression($compression);
|
$imagick->setImageCompression($compression);
|
||||||
|
|
||||||
|
if ($this->interlaced) {
|
||||||
|
$imagick->setInterlaceScheme(Imagick::INTERLACE_LINE);
|
||||||
|
}
|
||||||
|
|
||||||
return new EncodedImage($imagick->getImagesBlob(), 'image/gif');
|
return new EncodedImage($imagick->getImagesBlob(), 'image/gif');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -37,6 +37,10 @@ class JpegEncoder extends GenericJpegEncoder implements SpecializedInterface
|
|||||||
$imagick->setImageCompressionQuality($this->quality);
|
$imagick->setImageCompressionQuality($this->quality);
|
||||||
$imagick->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE);
|
$imagick->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE);
|
||||||
|
|
||||||
|
if ($this->progressive) {
|
||||||
|
$imagick->setInterlaceScheme(Imagick::INTERLACE_PLANE);
|
||||||
|
}
|
||||||
|
|
||||||
return new EncodedImage($imagick->getImagesBlob(), 'image/jpeg');
|
return new EncodedImage($imagick->getImagesBlob(), 'image/jpeg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,17 +11,17 @@ use Intervention\Image\Interfaces\ImageInterface;
|
|||||||
|
|
||||||
class FileExtensionEncoder extends AutoEncoder
|
class FileExtensionEncoder extends AutoEncoder
|
||||||
{
|
{
|
||||||
|
protected array $options = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new encoder instance to encode to format of given file extension
|
* Create new encoder instance to encode to format of given file extension
|
||||||
*
|
*
|
||||||
* @param null|string $extension Target file extension for example "png"
|
* @param null|string $extension Target file extension for example "png"
|
||||||
* @param int $quality
|
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(public ?string $extension = null, mixed ...$options)
|
||||||
public ?string $extension = null,
|
{
|
||||||
public int $quality = self::DEFAULT_QUALITY
|
$this->options = $options;
|
||||||
) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,15 +52,15 @@ class FileExtensionEncoder extends AutoEncoder
|
|||||||
}
|
}
|
||||||
|
|
||||||
return match (strtolower($extension)) {
|
return match (strtolower($extension)) {
|
||||||
'webp' => new WebpEncoder(quality: $this->quality),
|
'webp' => new WebpEncoder(...$this->options),
|
||||||
'avif' => new AvifEncoder(quality: $this->quality),
|
'avif' => new AvifEncoder(...$this->options),
|
||||||
'jpeg', 'jpg' => new JpegEncoder(quality: $this->quality),
|
'jpeg', 'jpg' => new JpegEncoder(...$this->options),
|
||||||
'bmp' => new BmpEncoder(),
|
'bmp' => new BmpEncoder(...$this->options),
|
||||||
'gif' => new GifEncoder(),
|
'gif' => new GifEncoder(...$this->options),
|
||||||
'png' => new PngEncoder(),
|
'png' => new PngEncoder(...$this->options),
|
||||||
'tiff', 'tif' => new TiffEncoder(quality: $this->quality),
|
'tiff', 'tif' => new TiffEncoder(...$this->options),
|
||||||
'jp2', 'j2k', 'jpf', 'jpm', 'jpg2', 'j2c', 'jpc', 'jpx' => new Jpeg2000Encoder(quality: $this->quality),
|
'jp2', 'j2k', 'jpf', 'jpm', 'jpg2', 'j2c', 'jpc', 'jpx' => new Jpeg2000Encoder(...$this->options),
|
||||||
'heic', 'heif' => new HeicEncoder(quality: $this->quality),
|
'heic', 'heif' => new HeicEncoder(...$this->options),
|
||||||
default => throw new EncoderException('No encoder found for file extension (' . $extension . ').'),
|
default => throw new EncoderException('No encoder found for file extension (' . $extension . ').'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -13,14 +13,13 @@ class FilePathEncoder extends FileExtensionEncoder
|
|||||||
* Create new encoder instance to encode to format of file extension in given path
|
* Create new encoder instance to encode to format of file extension in given path
|
||||||
*
|
*
|
||||||
* @param null|string $path
|
* @param null|string $path
|
||||||
* @param int $quality
|
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct(protected ?string $path = null, public int $quality = self::DEFAULT_QUALITY)
|
public function __construct(protected ?string $path = null, mixed ...$options)
|
||||||
{
|
{
|
||||||
parent::__construct(
|
parent::__construct(
|
||||||
is_null($path) ? $path : pathinfo($path, PATHINFO_EXTENSION),
|
is_null($path) ? $path : pathinfo($path, PATHINFO_EXTENSION),
|
||||||
$quality
|
...$options
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@ use Intervention\Image\Drivers\SpecializableEncoder;
|
|||||||
|
|
||||||
class GifEncoder extends SpecializableEncoder
|
class GifEncoder extends SpecializableEncoder
|
||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct(public bool $interlaced = false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,9 @@ use Intervention\Image\Drivers\SpecializableEncoder;
|
|||||||
|
|
||||||
class JpegEncoder extends SpecializableEncoder
|
class JpegEncoder extends SpecializableEncoder
|
||||||
{
|
{
|
||||||
public function __construct(public int $quality = self::DEFAULT_QUALITY)
|
public function __construct(
|
||||||
{
|
public int $quality = self::DEFAULT_QUALITY,
|
||||||
|
public bool $progressive = false
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,17 +12,17 @@ use Intervention\Image\Interfaces\ImageInterface;
|
|||||||
|
|
||||||
class MediaTypeEncoder extends AbstractEncoder
|
class MediaTypeEncoder extends AbstractEncoder
|
||||||
{
|
{
|
||||||
|
protected array $options = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new encoder instance
|
* Create new encoder instance
|
||||||
*
|
*
|
||||||
* @param null|string $mediaType Target media type for example "image/jpeg"
|
* @param null|string $mediaType Target media type for example "image/jpeg"
|
||||||
* @param int $quality
|
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(public ?string $mediaType = null, mixed ...$options)
|
||||||
public ?string $mediaType = null,
|
{
|
||||||
public int $quality = self::DEFAULT_QUALITY
|
$this->options = $options;
|
||||||
) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,12 +50,12 @@ class MediaTypeEncoder extends AbstractEncoder
|
|||||||
{
|
{
|
||||||
return match (strtolower($mediaType)) {
|
return match (strtolower($mediaType)) {
|
||||||
'image/webp',
|
'image/webp',
|
||||||
'image/x-webp' => new WebpEncoder(quality: $this->quality),
|
'image/x-webp' => new WebpEncoder(...$this->options),
|
||||||
'image/avif',
|
'image/avif',
|
||||||
'image/x-avif' => new AvifEncoder(quality: $this->quality),
|
'image/x-avif' => new AvifEncoder(...$this->options),
|
||||||
'image/jpeg',
|
'image/jpeg',
|
||||||
'image/jpg',
|
'image/jpg',
|
||||||
'image/pjpeg' => new JpegEncoder(quality: $this->quality),
|
'image/pjpeg' => new JpegEncoder(...$this->options),
|
||||||
'image/bmp',
|
'image/bmp',
|
||||||
'image/ms-bmp',
|
'image/ms-bmp',
|
||||||
'image/x-bitmap',
|
'image/x-bitmap',
|
||||||
@@ -63,16 +63,16 @@ class MediaTypeEncoder extends AbstractEncoder
|
|||||||
'image/x-ms-bmp',
|
'image/x-ms-bmp',
|
||||||
'image/x-win-bitmap',
|
'image/x-win-bitmap',
|
||||||
'image/x-windows-bmp',
|
'image/x-windows-bmp',
|
||||||
'image/x-xbitmap' => new BmpEncoder(),
|
'image/x-xbitmap' => new BmpEncoder(...$this->options),
|
||||||
'image/gif' => new GifEncoder(),
|
'image/gif' => new GifEncoder(...$this->options),
|
||||||
'image/png',
|
'image/png',
|
||||||
'image/x-png' => new PngEncoder(),
|
'image/x-png' => new PngEncoder(...$this->options),
|
||||||
'image/tiff' => new TiffEncoder(quality: $this->quality),
|
'image/tiff' => new TiffEncoder(...$this->options),
|
||||||
'image/jp2',
|
'image/jp2',
|
||||||
'image/jpx',
|
'image/jpx',
|
||||||
'image/jpm' => new Jpeg2000Encoder(quality: $this->quality),
|
'image/jpm' => new Jpeg2000Encoder(...$this->options),
|
||||||
'image/heic',
|
'image/heic',
|
||||||
'image/heif', => new HeicEncoder(quality: $this->quality),
|
'image/heif', => new HeicEncoder(...$this->options),
|
||||||
default => throw new EncoderException('No encoder found for media type (' . $mediaType . ').'),
|
default => throw new EncoderException('No encoder found for media type (' . $mediaType . ').'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -299,7 +299,7 @@ final class Image implements ImageInterface
|
|||||||
*
|
*
|
||||||
* @see ImageInterface::save()
|
* @see ImageInterface::save()
|
||||||
*/
|
*/
|
||||||
public function save(?string $path = null, ...$options): ImageInterface
|
public function save(?string $path = null, mixed ...$options): ImageInterface
|
||||||
{
|
{
|
||||||
$path = is_null($path) ? $this->origin()->filePath() : $path;
|
$path = is_null($path) ? $this->origin()->filePath() : $path;
|
||||||
|
|
||||||
|
@@ -88,7 +88,7 @@ interface ImageInterface extends IteratorAggregate, Countable
|
|||||||
* @throws RuntimeException
|
* @throws RuntimeException
|
||||||
* @return ImageInterface
|
* @return ImageInterface
|
||||||
*/
|
*/
|
||||||
public function save(?string $path = null, ...$options): self;
|
public function save(?string $path = null, mixed ...$options): self;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply given modifier to current image
|
* Apply given modifier to current image
|
||||||
|
@@ -35,4 +35,22 @@ abstract class ImagickTestCase extends BaseTestCase
|
|||||||
new Core($imagick)
|
new Core($imagick)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function createTestAnimation(): Image
|
||||||
|
{
|
||||||
|
$imagick = new Imagick();
|
||||||
|
$imagick->setFormat('gif');
|
||||||
|
|
||||||
|
for ($i = 0; $i < 3; $i++) {
|
||||||
|
$frame = new Imagick();
|
||||||
|
$frame->newImage(3, 2, new ImagickPixel('rgb(255, 0, 0)'), 'gif');
|
||||||
|
$frame->setImageDelay(10);
|
||||||
|
$imagick->addImage($frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Image(
|
||||||
|
new Driver(),
|
||||||
|
new Core($imagick)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
57
tests/Traits/CanDetectProgressiveJpeg.php
Normal file
57
tests/Traits/CanDetectProgressiveJpeg.php
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Intervention\Image\Tests\Traits;
|
||||||
|
|
||||||
|
use Intervention\Image\Traits\CanBuildFilePointer;
|
||||||
|
|
||||||
|
trait CanDetectProgressiveJpeg
|
||||||
|
{
|
||||||
|
use CanBuildFilePointer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given image data is progressive encoded Jpeg format
|
||||||
|
*
|
||||||
|
* @param string $imagedata
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function isProgressiveJpeg(string $imagedata): bool
|
||||||
|
{
|
||||||
|
$f = $this->buildFilePointer($imagedata);
|
||||||
|
|
||||||
|
while (!feof($f)) {
|
||||||
|
if (unpack('C', fread($f, 1))[1] !== 0xff) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$blockType = unpack('C', fread($f, 1))[1];
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case $blockType == 0xd8:
|
||||||
|
case $blockType >= 0xd0 && $blockType <= 0xd7:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case $blockType == 0xc0:
|
||||||
|
fclose($f);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case $blockType == 0xc2:
|
||||||
|
fclose($f);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case $blockType == 0xd9:
|
||||||
|
break 2;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$blockSize = unpack('n', fread($f, 2))[1];
|
||||||
|
fseek($f, $blockSize - 2, SEEK_CUR);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($f);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Intervention\Image\Tests\Unit\Drivers\Gd\Encoders;
|
namespace Intervention\Image\Tests\Unit\Drivers\Gd\Encoders;
|
||||||
|
|
||||||
|
use Intervention\Gif\Decoder;
|
||||||
use PHPUnit\Framework\Attributes\CoversClass;
|
use PHPUnit\Framework\Attributes\CoversClass;
|
||||||
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
|
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
|
||||||
use Intervention\Image\Encoders\GifEncoder;
|
use Intervention\Image\Encoders\GifEncoder;
|
||||||
@@ -20,5 +21,30 @@ final class GifEncoderTest extends GdTestCase
|
|||||||
$encoder = new GifEncoder();
|
$encoder = new GifEncoder();
|
||||||
$result = $encoder->encode($image);
|
$result = $encoder->encode($image);
|
||||||
$this->assertMediaType('image/gif', (string) $result);
|
$this->assertMediaType('image/gif', (string) $result);
|
||||||
|
$this->assertFalse(
|
||||||
|
Decoder::decode((string) $result)->getFirstFrame()->getImageDescriptor()->isInterlaced()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEncodeInterlaced(): void
|
||||||
|
{
|
||||||
|
$image = $this->createTestImage(3, 2);
|
||||||
|
$encoder = new GifEncoder(interlaced: true);
|
||||||
|
$result = $encoder->encode($image);
|
||||||
|
$this->assertMediaType('image/gif', (string) $result);
|
||||||
|
$this->assertTrue(
|
||||||
|
Decoder::decode((string) $result)->getFirstFrame()->getImageDescriptor()->isInterlaced()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEncodeInterlacedAnimation(): void
|
||||||
|
{
|
||||||
|
$image = $this->createTestAnimation(3, 2);
|
||||||
|
$encoder = new GifEncoder(interlaced: true);
|
||||||
|
$result = $encoder->encode($image);
|
||||||
|
$this->assertMediaType('image/gif', (string) $result);
|
||||||
|
$this->assertTrue(
|
||||||
|
Decoder::decode((string) $result)->getFirstFrame()->getImageDescriptor()->isInterlaced()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,12 +8,15 @@ use PHPUnit\Framework\Attributes\CoversClass;
|
|||||||
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
|
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
|
||||||
use Intervention\Image\Encoders\JpegEncoder;
|
use Intervention\Image\Encoders\JpegEncoder;
|
||||||
use Intervention\Image\Tests\GdTestCase;
|
use Intervention\Image\Tests\GdTestCase;
|
||||||
|
use Intervention\Image\Tests\Traits\CanDetectProgressiveJpeg;
|
||||||
|
|
||||||
#[RequiresPhpExtension('gd')]
|
#[RequiresPhpExtension('gd')]
|
||||||
#[CoversClass(\Intervention\Image\Encoders\JpegEncoder::class)]
|
#[CoversClass(\Intervention\Image\Encoders\JpegEncoder::class)]
|
||||||
#[CoversClass(\Intervention\Image\Drivers\Gd\Encoders\JpegEncoder::class)]
|
#[CoversClass(\Intervention\Image\Drivers\Gd\Encoders\JpegEncoder::class)]
|
||||||
final class JpegEncoderTest extends GdTestCase
|
final class JpegEncoderTest extends GdTestCase
|
||||||
{
|
{
|
||||||
|
use CanDetectProgressiveJpeg;
|
||||||
|
|
||||||
public function testEncode(): void
|
public function testEncode(): void
|
||||||
{
|
{
|
||||||
$image = $this->createTestImage(3, 2);
|
$image = $this->createTestImage(3, 2);
|
||||||
@@ -21,4 +24,13 @@ final class JpegEncoderTest extends GdTestCase
|
|||||||
$result = $encoder->encode($image);
|
$result = $encoder->encode($image);
|
||||||
$this->assertMediaType('image/jpeg', (string) $result);
|
$this->assertMediaType('image/jpeg', (string) $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testEncodeProgressive(): void
|
||||||
|
{
|
||||||
|
$image = $this->createTestImage(3, 2);
|
||||||
|
$encoder = new JpegEncoder(progressive: true);
|
||||||
|
$result = $encoder->encode($image);
|
||||||
|
$this->assertMediaType('image/jpeg', (string) $result);
|
||||||
|
$this->assertTrue($this->isProgressiveJpeg((string) $result));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Intervention\Image\Tests\Unit\Drivers\Imagick\Encoders;
|
namespace Intervention\Image\Tests\Unit\Drivers\Imagick\Encoders;
|
||||||
|
|
||||||
|
use Intervention\Gif\Decoder;
|
||||||
use PHPUnit\Framework\Attributes\CoversClass;
|
use PHPUnit\Framework\Attributes\CoversClass;
|
||||||
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
|
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
|
||||||
use Intervention\Image\Encoders\GifEncoder;
|
use Intervention\Image\Encoders\GifEncoder;
|
||||||
@@ -20,5 +21,30 @@ final class GifEncoderTest extends ImagickTestCase
|
|||||||
$encoder = new GifEncoder();
|
$encoder = new GifEncoder();
|
||||||
$result = $encoder->encode($image);
|
$result = $encoder->encode($image);
|
||||||
$this->assertMediaType('image/gif', (string) $result);
|
$this->assertMediaType('image/gif', (string) $result);
|
||||||
|
$this->assertFalse(
|
||||||
|
Decoder::decode((string) $result)->getFirstFrame()->getImageDescriptor()->isInterlaced()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEncodeInterlaced(): void
|
||||||
|
{
|
||||||
|
$image = $this->createTestImage(3, 2);
|
||||||
|
$encoder = new GifEncoder(interlaced: true);
|
||||||
|
$result = $encoder->encode($image);
|
||||||
|
$this->assertMediaType('image/gif', (string) $result);
|
||||||
|
$this->assertTrue(
|
||||||
|
Decoder::decode((string) $result)->getFirstFrame()->getImageDescriptor()->isInterlaced()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEncodeInterlacedAnimation(): void
|
||||||
|
{
|
||||||
|
$image = $this->createTestAnimation();
|
||||||
|
$encoder = new GifEncoder(interlaced: true);
|
||||||
|
$result = $encoder->encode($image);
|
||||||
|
$this->assertMediaType('image/gif', (string) $result);
|
||||||
|
$this->assertTrue(
|
||||||
|
Decoder::decode((string) $result)->getFirstFrame()->getImageDescriptor()->isInterlaced()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,12 +8,15 @@ use PHPUnit\Framework\Attributes\CoversClass;
|
|||||||
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
|
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
|
||||||
use Intervention\Image\Encoders\JpegEncoder;
|
use Intervention\Image\Encoders\JpegEncoder;
|
||||||
use Intervention\Image\Tests\ImagickTestCase;
|
use Intervention\Image\Tests\ImagickTestCase;
|
||||||
|
use Intervention\Image\Tests\Traits\CanDetectProgressiveJpeg;
|
||||||
|
|
||||||
#[RequiresPhpExtension('imagick')]
|
#[RequiresPhpExtension('imagick')]
|
||||||
#[CoversClass(\Intervention\Image\Encoders\JpegEncoder::class)]
|
#[CoversClass(\Intervention\Image\Encoders\JpegEncoder::class)]
|
||||||
#[CoversClass(\Intervention\Image\Drivers\Imagick\Encoders\JpegEncoder::class)]
|
#[CoversClass(\Intervention\Image\Drivers\Imagick\Encoders\JpegEncoder::class)]
|
||||||
final class JpegEncoderTest extends ImagickTestCase
|
final class JpegEncoderTest extends ImagickTestCase
|
||||||
{
|
{
|
||||||
|
use CanDetectProgressiveJpeg;
|
||||||
|
|
||||||
public function testEncode(): void
|
public function testEncode(): void
|
||||||
{
|
{
|
||||||
$image = $this->createTestImage(3, 2);
|
$image = $this->createTestImage(3, 2);
|
||||||
@@ -21,4 +24,13 @@ final class JpegEncoderTest extends ImagickTestCase
|
|||||||
$result = $encoder->encode($image);
|
$result = $encoder->encode($image);
|
||||||
$this->assertMediaType('image/jpeg', (string) $result);
|
$this->assertMediaType('image/jpeg', (string) $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testEncodeProgressive(): void
|
||||||
|
{
|
||||||
|
$image = $this->createTestImage(3, 2);
|
||||||
|
$encoder = new JpegEncoder(progressive: true);
|
||||||
|
$result = $encoder->encode($image);
|
||||||
|
$this->assertMediaType('image/jpeg', (string) $result);
|
||||||
|
$this->assertTrue($this->isProgressiveJpeg((string) $result));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user