Skip to content

Commit b9fe001

Browse files
committed
feat: AVIF image support
1 parent 07fa170 commit b9fe001

File tree

8 files changed

+66
-16
lines changed

8 files changed

+66
-16
lines changed

app/Config/Mimes.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ class Mimes
259259
'image/x-png',
260260
],
261261
'webp' => 'image/webp',
262+
'avif' => 'image/avif',
262263
'tif' => 'image/tiff',
263264
'tiff' => 'image/tiff',
264265
'css' => [

app/Config/Publisher.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ class Publisher extends BasePublisher
2323
*/
2424
public $restrictions = [
2525
ROOTPATH => '*',
26-
FCPATH => '#\.(s?css|js|map|html?|xml|json|webmanifest|ttf|eot|woff2?|gif|jpe?g|tiff?|png|webp|bmp|ico|svg)$#i',
26+
FCPATH => '#\.(s?css|js|map|html?|xml|json|webmanifest|ttf|eot|woff2?|gif|jpe?g|tiff?|png|webp|avif|bmp|ico|svg)$#i',
2727
];
2828
}

system/Config/Publisher.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class Publisher extends BaseConfig
3232
*/
3333
public $restrictions = [
3434
ROOTPATH => '*',
35-
FCPATH => '#\.(?css|js|map|htm?|xml|json|webmanifest|tff|eot|woff?|gif|jpe?g|tiff?|png|webp|bmp|ico|svg)$#i',
35+
FCPATH => '#\.(?css|js|map|htm?|xml|json|webmanifest|tff|eot|woff?|gif|jpe?g|tiff?|png|webp|avif|bmp|ico|svg)$#i',
3636
];
3737

3838
/**

system/Images/Handlers/BaseHandler.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ abstract class BaseHandler implements ImageHandlerInterface
115115
protected $supportTransparency = [
116116
IMAGETYPE_PNG,
117117
IMAGETYPE_WEBP,
118+
IMAGETYPE_AVIF,
118119
];
119120

120121
/**
@@ -682,7 +683,7 @@ abstract public function getVersion();
682683
*
683684
* @return bool
684685
*/
685-
abstract public function save(?string $target = null, int $quality = 90);
686+
abstract public function save(?string $target = null, int $quality = 90, int $speed = -1);
686687

687688
/**
688689
* Does the driver-specific processing of the image.

system/Images/Handlers/GDHandler.php

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ protected function process(string $action)
178178

179179
$dest = $create($this->width, $this->height);
180180

181-
// for png and webp we can actually preserve transparency
181+
// for png, webp and avif we can actually preserve transparency
182182
if (in_array($this->image()->imageType, $this->supportTransparency, true)) {
183183
imagealphablending($dest, false);
184184
imagesavealpha($dest, true);
@@ -202,7 +202,7 @@ protected function process(string $action)
202202
*
203203
* @param non-empty-string|null $target
204204
*/
205-
public function save(?string $target = null, int $quality = 90): bool
205+
public function save(?string $target = null, int $quality = 90, int $speed = -1): bool
206206
{
207207
$original = $target;
208208
$target = ($target === null || $target === '') ? $this->image()->getPathname() : $target;
@@ -222,7 +222,7 @@ public function save(?string $target = null, int $quality = 90): bool
222222

223223
$this->ensureResource();
224224

225-
// for png and webp we can actually preserve transparency
225+
// for png, webp and avif we can actually preserve transparency
226226
if (in_array($this->image()->imageType, $this->supportTransparency, true)) {
227227
imagepalettetotruecolor($this->resource);
228228
imagealphablending($this->resource, false);
@@ -270,6 +270,16 @@ public function save(?string $target = null, int $quality = 90): bool
270270
}
271271
break;
272272

273+
case IMAGETYPE_AVIF:
274+
if (! function_exists('imageavif')) {
275+
throw ImageException::forInvalidImageCreate(lang('Images.avifNotSupported'));
276+
}
277+
278+
if (! @imageavif($this->resource, $target, $quality, $speed)) {
279+
throw ImageException::forSaveFailed();
280+
}
281+
break;
282+
273283
default:
274284
throw ImageException::forInvalidImageCreate();
275285
}
@@ -361,6 +371,13 @@ protected function getImageResource(string $path, int $imageType)
361371

362372
return imagecreatefromwebp($path);
363373

374+
case IMAGETYPE_AVIF:
375+
if (! function_exists('imagecreatefromavif')) {
376+
throw ImageException::forInvalidImageCreate(lang('Images.avifNotSupported'));
377+
}
378+
379+
return imagecreatefromavif($path);
380+
364381
default:
365382
throw ImageException::forInvalidImageCreate('Ima');
366383
}

system/Images/Image.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ public function getProperties(bool $return = false)
113113
IMAGETYPE_JPEG => 'jpeg',
114114
IMAGETYPE_PNG => 'png',
115115
IMAGETYPE_WEBP => 'webp',
116+
IMAGETYPE_AVIF => 'avif',
116117
];
117118

118119
$mime = 'image/' . ($types[$vals[2]] ?? 'jpg');

system/Language/en/Images.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
'jpgNotSupported' => 'JPG images are not supported.',
2121
'pngNotSupported' => 'PNG images are not supported.',
2222
'webpNotSupported' => 'WEBP images are not supported.',
23+
'avifNotSupported' => 'AVIF images are not supported.',
2324
'fileNotSupported' => 'The supplied file is not a supported image type.',
2425
'unsupportedImageCreate' => 'Your server does not support the required functionality to process this type of image.',
2526
'jpgOrPngRequired' => 'The image resize protocol specified in your preferences only works with JPEG or PNG image types.',

tests/system/Images/GDHandlerTest.php

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,14 @@ public function testMoreText(): void
319319

320320
public function testImageCreation(): void
321321
{
322-
foreach (['gif', 'jpeg', 'png', 'webp'] as $type) {
322+
foreach (['gif', 'jpeg', 'png', 'webp', 'avif'] as $type) {
323323
if ($type === 'webp' && ! function_exists('imagecreatefromwebp')) {
324324
$this->expectException(ImageException::class);
325-
$this->expectExceptionMessage('Your server does not support the GD function required to process this type of image.');
325+
$this->expectExceptionMessage('Your server does not support the GD function required to process a webp image.');
326+
}
327+
if ($type === 'avif' && ! function_exists('imagecreatefromavif')) {
328+
$this->expectException(ImageException::class);
329+
$this->expectExceptionMessage('Your server does not support the GD function required to process an avif image.');
326330
}
327331

328332
$this->handler->withFile($this->origin . 'ci-logo.' . $type);
@@ -334,10 +338,14 @@ public function testImageCreation(): void
334338

335339
public function testImageCopy(): void
336340
{
337-
foreach (['gif', 'jpeg', 'png', 'webp'] as $type) {
341+
foreach (['gif', 'jpeg', 'png', 'webp', 'avif'] as $type) {
338342
if ($type === 'webp' && ! function_exists('imagecreatefromwebp')) {
339343
$this->expectException(ImageException::class);
340-
$this->expectExceptionMessage('Your server does not support the GD function required to process this type of image.');
344+
$this->expectExceptionMessage('Your server does not support the GD function required to process a webp image.');
345+
}
346+
if ($type === 'avif' && ! function_exists('imagecreatefromavif')) {
347+
$this->expectException(ImageException::class);
348+
$this->expectExceptionMessage('Your server does not support the GD function required to process an avif image.');
341349
}
342350

343351
$this->handler->withFile($this->origin . 'ci-logo.' . $type);
@@ -353,9 +361,13 @@ public function testImageCopy(): void
353361

354362
public function testImageCopyWithNoTargetAndMaxQuality(): void
355363
{
356-
foreach (['gif', 'jpeg', 'png', 'webp'] as $type) {
364+
foreach (['gif', 'jpeg', 'png', 'webp', 'avif'] as $type) {
357365
$this->handler->withFile($this->origin . 'ci-logo.' . $type);
358-
$this->handler->save(null, 100);
366+
if($type === 'avif') {
367+
$this->handler->save(null, 100, 10);
368+
} else {
369+
$this->handler->save(null, 100);
370+
}
359371
$this->assertFileExists($this->origin . 'ci-logo.' . $type);
360372

361373
$this->assertSame(
@@ -367,10 +379,14 @@ public function testImageCopyWithNoTargetAndMaxQuality(): void
367379

368380
public function testImageCompressionGetResource(): void
369381
{
370-
foreach (['gif', 'jpeg', 'png', 'webp'] as $type) {
382+
foreach (['gif', 'jpeg', 'png', 'webp', 'avif'] as $type) {
371383
if ($type === 'webp' && ! function_exists('imagecreatefromwebp')) {
372384
$this->expectException(ImageException::class);
373-
$this->expectExceptionMessage('Your server does not support the GD function required to process this type of image.');
385+
$this->expectExceptionMessage('Your server does not support the GD function required to process a webp image.');
386+
}
387+
if ($type === 'avif' && ! function_exists('imagecreatefromavif')) {
388+
$this->expectException(ImageException::class);
389+
$this->expectExceptionMessage('Your server does not support the GD function required to process an avif image.');
374390
}
375391

376392
$this->handler->withFile($this->origin . 'ci-logo.' . $type);
@@ -387,10 +403,14 @@ public function testImageCompressionGetResource(): void
387403

388404
public function testImageCompressionWithResource(): void
389405
{
390-
foreach (['gif', 'jpeg', 'png', 'webp'] as $type) {
406+
foreach (['gif', 'jpeg', 'png', 'webp', 'avif'] as $type) {
391407
if ($type === 'webp' && ! function_exists('imagecreatefromwebp')) {
392408
$this->expectException(ImageException::class);
393-
$this->expectExceptionMessage('Your server does not support the GD function required to process this type of image.');
409+
$this->expectExceptionMessage('Your server does not support the GD function required to process a webp image.');
410+
}
411+
if ($type === 'avif' && ! function_exists('imagecreatefromavif')) {
412+
$this->expectException(ImageException::class);
413+
$this->expectExceptionMessage('Your server does not support the GD function required to process an avif image.');
394414
}
395415

396416
$this->handler->withFile($this->origin . 'ci-logo.' . $type)
@@ -423,6 +443,15 @@ public function testImageConvertPngToWebp(): void
423443
$this->assertSame(IMAGETYPE_WEBP, exif_imagetype($saved));
424444
}
425445

446+
public function testImageConvertPngToAvif(): void
447+
{
448+
$this->handler->withFile($this->origin . 'rocket.png');
449+
$this->handler->convert(IMAGETYPE_AVIF);
450+
$saved = $this->start . 'work/rocket.avif';
451+
$this->handler->save($saved);
452+
$this->assertSame(IMAGETYPE_AVIF, exif_imagetype($saved));
453+
}
454+
426455
public function testImageReorientLandscape(): void
427456
{
428457
for ($i = 0; $i <= 8; $i++) {

0 commit comments

Comments
 (0)