Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/Illuminate/Image/Drivers/InterventionDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@
use Illuminate\Image\Transformations\Scale;
use Illuminate\Image\Transformations\Sharpen;
use Intervention\Image\Direction;
use Intervention\Image\Encoders\AvifEncoder;
use Intervention\Image\Encoders\BmpEncoder;
use Intervention\Image\Encoders\GifEncoder;
use Intervention\Image\Encoders\JpegEncoder;
use Intervention\Image\Encoders\MediaTypeEncoder;
use Intervention\Image\Encoders\PngEncoder;
use Intervention\Image\Encoders\WebpEncoder;
use Intervention\Image\ImageManager;

Expand Down Expand Up @@ -111,6 +115,10 @@ public function process(string $contents, ImagePipeline $pipeline): string
return $image->encode(match ($pipeline->output->format) {
'webp' => new WebpEncoder($quality),
'jpg', 'jpeg' => new JpegEncoder($quality),
'png' => new PngEncoder,
'gif' => new GifEncoder,
'avif' => new AvifEncoder($quality),
'bmp' => new BmpEncoder,
})->toString();
}

Expand Down
35 changes: 34 additions & 1 deletion src/Illuminate/Image/Image.php
Original file line number Diff line number Diff line change
Expand Up @@ -258,14 +258,46 @@ public function toJpeg(): static
return $this->toJpg();
}

/**
* Convert the image to PNG format.
*/
public function toPng(): static
{
return $this->toFormat('png');
}

/**
* Convert the image to GIF format.
*/
public function toGif(): static
{
return $this->toFormat('gif');
}

/**
* Convert the image to AVIF format.
*/
public function toAvif(): static
{
return $this->toFormat('avif');
}

/**
* Convert the image to BMP format.
*/
public function toBmp(): static
{
return $this->toFormat('bmp');
}

/**
* Set the output format.
*
* @throws ImageException
*/
protected function toFormat(string $format): static
{
if (! in_array($format, ['webp', 'jpg', 'jpeg'])) {
if (! in_array($format, ['webp', 'jpg', 'jpeg', 'png', 'gif', 'avif', 'bmp'])) {
throw new ImageException("The [{$format}] format is not supported.");
}

Expand Down Expand Up @@ -390,6 +422,7 @@ public function extension(): string
'image/png' => 'png',
'image/gif' => 'gif',
'image/webp' => 'webp',
'image/avif' => 'avif',
'image/bmp' => 'bmp',
'image/svg+xml' => 'svg',
'image/tiff' => 'tiff',
Expand Down
2 changes: 1 addition & 1 deletion src/Illuminate/Image/ImageOutputOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class ImageOutputOptions
/**
* The output format.
*
* @var 'webp'|'jpg'|'jpeg'|null
* @var 'webp'|'jpg'|'jpeg'|'png'|'gif'|'avif'|'bmp'|null
*/
public ?string $format = null;

Expand Down
48 changes: 48 additions & 0 deletions tests/Image/Drivers/GdDriverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,54 @@ public function test_processes_optimize_to_jpeg()
$this->assertSame(IMAGETYPE_JPEG, getimagesizefromstring($result)[2]);
}

public function test_processes_optimize_to_png()
{
$driver = new GdDriver;

$pipeline = $this->pipeline(format: 'png');

$result = $driver->process($this->fakeImageContents(), $pipeline);

$this->assertSame(IMAGETYPE_PNG, getimagesizefromstring($result)[2]);
}

public function test_processes_optimize_to_gif()
{
$driver = new GdDriver;

$pipeline = $this->pipeline(format: 'gif');

$result = $driver->process($this->fakeImageContents(), $pipeline);

$this->assertSame(IMAGETYPE_GIF, getimagesizefromstring($result)[2]);
}

public function test_processes_optimize_to_avif()
{
if (! function_exists('imageavif')) {
$this->markTestSkipped('The GD extension was not compiled with AVIF support.');
}

$driver = new GdDriver;

$pipeline = $this->pipeline(format: 'avif');

$result = $driver->process($this->fakeImageContents(), $pipeline);

$this->assertSame(IMAGETYPE_AVIF, getimagesizefromstring($result)[2]);
}

public function test_processes_optimize_to_bmp()
{
$driver = new GdDriver;

$pipeline = $this->pipeline(format: 'bmp');

$result = $driver->process($this->fakeImageContents(), $pipeline);

$this->assertSame(IMAGETYPE_BMP, getimagesizefromstring($result)[2]);
}

public function test_processes_cover_and_optimize_together()
{
$driver = new GdDriver;
Expand Down
48 changes: 48 additions & 0 deletions tests/Image/Drivers/ImagickDriverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,54 @@ public function test_processes_optimize_to_jpeg()
$this->assertSame(IMAGETYPE_JPEG, getimagesizefromstring($result)[2]);
}

public function test_processes_optimize_to_png()
{
$driver = new ImagickDriver;

$pipeline = $this->pipeline(format: 'png');

$result = $driver->process($this->fakeImageContents(), $pipeline);

$this->assertSame(IMAGETYPE_PNG, getimagesizefromstring($result)[2]);
}

public function test_processes_optimize_to_gif()
{
$driver = new ImagickDriver;

$pipeline = $this->pipeline(format: 'gif');

$result = $driver->process($this->fakeImageContents(), $pipeline);

$this->assertSame(IMAGETYPE_GIF, getimagesizefromstring($result)[2]);
}

public function test_processes_optimize_to_avif()
{
$driver = new ImagickDriver;

$pipeline = $this->pipeline(format: 'avif');

$result = $driver->process($this->fakeImageContents(), $pipeline);

// PHP's getimagesizefromstring()/finfo AVIF detection varies by the
// libavif/libmagic versions installed, so we assert on the ISOBMFF
// "ftyp" box and brand directly instead of relying on either.
$this->assertStringContainsString('ftyp', substr($result, 0, 16));
$this->assertMatchesRegularExpression('/avif|avis|mif1/', substr($result, 0, 32));
}

public function test_processes_optimize_to_bmp()
{
$driver = new ImagickDriver;

$pipeline = $this->pipeline(format: 'bmp');

$result = $driver->process($this->fakeImageContents(), $pipeline);

$this->assertSame(IMAGETYPE_BMP, getimagesizefromstring($result)[2]);
}

public function test_processes_cover_and_optimize_together()
{
$driver = new ImagickDriver;
Expand Down
105 changes: 93 additions & 12 deletions tests/Image/ImageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,34 @@ public function test_to_jpg_returns_new_instance()
$this->assertNotSame($image, $image->toJpg());
}

public function test_to_png_returns_new_instance()
{
$image = $this->makeImage();

$this->assertNotSame($image, $image->toPng());
}

public function test_to_gif_returns_new_instance()
{
$image = $this->makeImage();

$this->assertNotSame($image, $image->toGif());
}

public function test_to_avif_returns_new_instance()
{
$image = $this->makeImage();

$this->assertNotSame($image, $image->toAvif());
}

public function test_to_bmp_returns_new_instance()
{
$image = $this->makeImage();

$this->assertNotSame($image, $image->toBmp());
}

public function test_using_returns_new_instance()
{
$image = $this->makeImage();
Expand Down Expand Up @@ -221,6 +249,26 @@ public function test_extension_returns_jpg_for_jpeg()
$this->assertSame('jpg', $image->extension());
}

public function test_extension_returns_avif_for_avif()
{
if (! extension_loaded('imagick')) {
$this->markTestSkipped('The Imagick extension is not available.');
}

$imagick = new \Imagick;
$imagick->newImage(10, 10, 'red');
$imagick->setImageFormat('avif');
$contents = $imagick->getImageBlob();

if ((new \finfo(FILEINFO_MIME_TYPE))->buffer($contents) !== 'image/avif') {
$this->markTestSkipped('The installed fileinfo database does not recognize AVIF.');
}

$image = new Image($contents);

$this->assertSame('avif', $image->extension());
}

public function test_dimensions_returns_width_and_height()
{
$image = new Image($this->fakeImageContents(300, 200));
Expand Down Expand Up @@ -298,9 +346,9 @@ public function test_optimize_throws_for_unsupported_format()
$image = $this->makeImage();

$this->expectException(ImageException::class);
$this->expectExceptionMessage('The [bmp] format is not supported.');
$this->expectExceptionMessage('The [tiff] format is not supported.');

$image->optimize('bmp');
$image->optimize('tiff');
}

public function test_quality_sets_option()
Expand Down Expand Up @@ -332,6 +380,34 @@ public function test_to_jpeg_is_alias_for_to_jpg()
$this->assertSame('jpg', $this->getOptions($image->toJpeg())->format);
}

public function test_to_png_sets_format()
{
$image = $this->makeImage();

$this->assertSame('png', $this->getOptions($image->toPng())->format);
}

public function test_to_gif_sets_format()
{
$image = $this->makeImage();

$this->assertSame('gif', $this->getOptions($image->toGif())->format);
}

public function test_to_avif_sets_format()
{
$image = $this->makeImage();

$this->assertSame('avif', $this->getOptions($image->toAvif())->format);
}

public function test_to_bmp_sets_format()
{
$image = $this->makeImage();

$this->assertSame('bmp', $this->getOptions($image->toBmp())->format);
}

public function test_quality_survives_format_conversion()
{
$image = $this->makeImage();
Expand Down Expand Up @@ -620,9 +696,16 @@ public function test_optimize_throws_for_jpg_with_wrong_spelling()
$image = $this->makeImage();

$this->expectException(ImageException::class);
$this->expectExceptionMessage('The [png] format is not supported.');
$this->expectExceptionMessage('The [jpge] format is not supported.');

$image->optimize('png');
$image->optimize('jpge');
}

public function test_optimize_allows_png()
{
$result = $this->makeImage()->optimize('png');

$this->assertSame('png', $this->getOptions($result)->format);
}

public function test_serialization_throws_exception()
Expand Down Expand Up @@ -819,20 +902,18 @@ public function test_optimize_sets_both_format_and_quality()
$this->assertSame(90, $options->quality);
}

public function test_optimize_throws_for_gif()
public function test_optimize_allows_gif()
{
$this->expectException(ImageException::class);
$this->expectExceptionMessage('The [gif] format is not supported.');
$result = $this->makeImage()->optimize('gif');

$this->makeImage()->optimize('gif');
$this->assertSame('gif', $this->getOptions($result)->format);
}

public function test_optimize_throws_for_avif()
public function test_optimize_allows_avif()
{
$this->expectException(ImageException::class);
$this->expectExceptionMessage('The [avif] format is not supported.');
$result = $this->makeImage()->optimize('avif');

$this->makeImage()->optimize('avif');
$this->assertSame('avif', $this->getOptions($result)->format);
}

public function test_optimize_allows_jpeg_spelling()
Expand Down
Loading