Skip to content

Commit

Permalink
Add AVIF support
Browse files Browse the repository at this point in the history
  • Loading branch information
adamsilverstein committed Jun 13, 2023
1 parent 0bfc842 commit 341a9e3
Show file tree
Hide file tree
Showing 18 changed files with 185 additions and 12 deletions.
5 changes: 5 additions & 0 deletions src/js/_enqueues/vendor/plupload/handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,11 @@ jQuery( document ).ready( function( $ ) {
wpQueueError( pluploadL10n.noneditable_image );
up.removeFile( file );
return;
} else if ( file.type === 'image/avif' && up.settings.avif_upload_error ) {
// Disallow uploading of AVIF images if the server cannot edit them.
wpQueueError( pluploadL10n.noneditable_image );
up.removeFile( file );
return;
}

fileQueued( file );
Expand Down
5 changes: 5 additions & 0 deletions src/js/_enqueues/vendor/plupload/wp-plupload.js
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,11 @@ window.wp = window.wp || {};
error( pluploadL10n.noneditable_image, {}, file, 'no-retry' );
up.removeFile( file );
return;
} else if ( file.type === 'image/avif' && up.settings.avif_upload_error ) {
// Disallow uploading of AVIF images if the server cannot edit them.
error( pluploadL10n.noneditable_image, {}, file, 'no-retry' );
up.removeFile( file );
return;
}

// Generate attributes for a new `Attachment` model.
Expand Down
5 changes: 3 additions & 2 deletions src/js/_enqueues/vendor/thickbox/thickbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,16 @@ function tb_show(caption, url, imageGroup) {//function called when the user clic
baseURL = url;
}

var urlString = /\.jpg$|\.jpeg$|\.png$|\.gif$|\.bmp$|\.webp$/;
var urlString = /\.jpg$|\.jpeg$|\.png$|\.gif$|\.bmp$|\.webp$|\.avif$/;
var urlType = baseURL.toLowerCase().match(urlString);

if(urlType == '.jpg' ||
urlType == '.jpeg' ||
urlType == '.png' ||
urlType == '.gif' ||
urlType == '.bmp' ||
urlType == '.webp'
urlType == '.webp' ||
urlType == '.avif'
){//code to show images

TB_PrevCaption = "";
Expand Down
2 changes: 1 addition & 1 deletion src/js/media/controllers/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ Library = wp.media.controller.State.extend(/** @lends wp.media.controller.Librar
isImageAttachment: function( attachment ) {
// If uploading, we know the filename but not the mime type.
if ( attachment.get('uploading') ) {
return /\.(jpe?g|png|gif|webp)$/i.test( attachment.get('filename') );
return /\.(jpe?g|png|gif|webp|avif)$/i.test( attachment.get('filename') );
}

return attachment.get('type') === 'image';
Expand Down
11 changes: 11 additions & 0 deletions src/wp-admin/includes/image-edit.php
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,12 @@ function wp_stream_image( $image, $mime_type, $attachment_id ) {
return imagewebp( $image, null, 90 );
}
return false;
case 'image/avif':
if ( function_exists( 'imageavif' ) ) {
header( 'Content-Type: image/avif' );
return imageavif( $image, null, 90 );
}
return false;
default:
return false;
}
Expand Down Expand Up @@ -491,6 +497,11 @@ function wp_save_image_file( $filename, $image, $mime_type, $post_id ) {
return imagewebp( $image, $filename );
}
return false;
case 'image/avif':
if ( function_exists( 'imageavif' ) ) {
return imageavif( $image, $filename );
}
return false;
default:
return false;
}
Expand Down
5 changes: 5 additions & 0 deletions src/wp-admin/includes/media.php
Original file line number Diff line number Diff line change
Expand Up @@ -2194,6 +2194,11 @@ function media_upload_form( $errors = null ) {
$plupload_init['webp_upload_error'] = true;
}

// Check if AVIF images can be edited.
if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/avif' ) ) ) {
$plupload_init['avif_upload_error'] = true;
}

/**
* Filters the default Plupload settings.
*
Expand Down
1 change: 1 addition & 0 deletions src/wp-admin/includes/schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -1247,6 +1247,7 @@ function populate_network_meta( $network_id, array $meta = array() ) {
'png',
'gif',
'webp',
'avif',
// Video.
'mov',
'avi',
Expand Down
Empty file.
24 changes: 22 additions & 2 deletions src/wp-includes/class-wp-image-editor-gd.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ public static function supports_mime_type( $mime_type ) {
return ( $image_types & IMG_GIF ) != 0;
case 'image/webp':
return ( $image_types & IMG_WEBP ) != 0;
}
case 'image/avif':
return ( $image_types & IMG_AVIF ) != 0;
}

return false;
}
Expand Down Expand Up @@ -111,6 +113,16 @@ function_exists( 'imagecreatefromwebp' ) &&
$this->image = @imagecreatefromstring( $file_contents );
}

// AVIF may not work with imagecreatefromstring().
if (
function_exists( 'imagecreatefromavif' ) &&
( 'image/avif' === wp_get_image_mime( $this->file ) )
) {
$this->image = @imagecreatefromavif( $this->file );
} else {
$this->image = @imagecreatefromstring( $file_contents );
}

if ( ! is_gd_image( $this->image ) ) {
return new WP_Error( 'invalid_image', __( 'File is not an image.' ), $this->file );
}
Expand Down Expand Up @@ -497,6 +509,10 @@ protected function _save( $image, $filename = null, $mime_type = null ) {
if ( ! function_exists( 'imagewebp' ) || ! $this->make_image( $filename, 'imagewebp', array( $image, $filename, $this->get_quality() ) ) ) {
return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
}
} elseif ( 'image/avif' == $mime_type ) {
if ( ! function_exists( 'imageavif' ) || ! $this->make_image( $filename, 'imageavif', array( $image, $filename, $this->get_quality() ) ) ) {
return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
}
} else {
return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
}
Expand Down Expand Up @@ -546,7 +562,11 @@ public function stream( $mime_type = null ) {
header( 'Content-Type: image/webp' );
return imagewebp( $this->image, null, $this->get_quality() );
}
// Fall back to the default if webp isn't supported.
case 'image/avif':
if ( function_exists( 'imageavif' ) ) {
header( 'Content-Type: image/avif' );
return imageavif( $this->image, null, $this->get_quality() );
}
default:
header( 'Content-Type: image/jpeg' );
return imagejpeg( $this->image, null, $this->get_quality() );
Expand Down
9 changes: 9 additions & 0 deletions src/wp-includes/class-wp-image-editor-imagick.php
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,15 @@ public function set_quality( $quality = null ) {
$this->image->setImageCompressionQuality( $quality );
}
break;
case 'image/avif':
$avif_info = wp_get_avif_info( $this->file );
if ( 'lossless' === $avif_info['type'] ) {
// Use quality 100 to trigger AVIF lossless.
$this->image->setImageCompressionQuality( 100 );
} else {
$this->image->setImageCompressionQuality( $quality );
}
break;
default:
$this->image->setImageCompressionQuality( $quality );
}
Expand Down
2 changes: 1 addition & 1 deletion src/wp-includes/class-wp-theme.php
Original file line number Diff line number Diff line change
Expand Up @@ -1204,7 +1204,7 @@ public function get_screenshot( $uri = 'uri' ) {
return false;
}

foreach ( array( 'png', 'gif', 'jpg', 'jpeg', 'webp' ) as $ext ) {
foreach ( array( 'png', 'gif', 'jpg', 'jpeg', 'webp', 'avif' ) as $ext ) {
if ( file_exists( $this->get_stylesheet_directory() . "/screenshot.$ext" ) ) {
$this->cache_add( 'screenshot', 'screenshot.' . $ext );
if ( 'relative' === $uri ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public function to_json() {
// Fake an attachment model - needs all fields used by template.
// Note that the default value must be a URL, NOT an attachment ID.
$ext = substr( $this->setting->default, -3 );
$type = in_array( $ext, array( 'jpg', 'png', 'gif', 'bmp', 'webp' ), true ) ? 'image' : 'document';
$type = in_array( $ext, array( 'jpg', 'png', 'gif', 'bmp', 'webp', 'avif' ), true ) ? 'image' : 'document';

$default_attachment = array(
'id' => 1,
Expand Down
6 changes: 5 additions & 1 deletion src/wp-includes/deprecated.php
Original file line number Diff line number Diff line change
Expand Up @@ -3326,7 +3326,9 @@ function gd_edit_image_support($mime_type) {
return (imagetypes() & IMG_GIF) != 0;
case 'image/webp':
return (imagetypes() & IMG_WEBP) != 0;
}
case 'image/avif':
return (imagetypes() & IMG_AVIF) != 0;
}
} else {
switch( $mime_type ) {
case 'image/jpeg':
Expand All @@ -3337,6 +3339,8 @@ function gd_edit_image_support($mime_type) {
return function_exists('imagecreatefromgif');
case 'image/webp':
return function_exists('imagecreatefromwebp');
case 'image/avif':
return function_exists('imagecreatefromavif');
}
}
return false;
Expand Down
2 changes: 1 addition & 1 deletion src/wp-includes/formatting.php
Original file line number Diff line number Diff line change
Expand Up @@ -3440,7 +3440,7 @@ function translate_smiley( $matches ) {

$matches = array();
$ext = preg_match( '/\.([^.]+)$/', $img, $matches ) ? strtolower( $matches[1] ) : false;
$image_exts = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'webp' );
$image_exts = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'webp', 'avif' );

// Don't convert smilies that aren't images - they're probably emoji.
if ( ! in_array( $ext, $image_exts, true ) ) {
Expand Down
20 changes: 19 additions & 1 deletion src/wp-includes/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -3104,6 +3104,7 @@ function wp_check_filetype_and_ext( $file, $filename, $mimes = null ) {
'image/bmp' => 'bmp',
'image/tiff' => 'tif',
'image/webp' => 'webp',
'image/avif' => 'avif',
)
);

Expand Down Expand Up @@ -3263,6 +3264,7 @@ function wp_check_filetype_and_ext( $file, $filename, $mimes = null ) {
*
* @since 4.7.1
* @since 5.8.0 Added support for WebP images.
* @since 6.4.0 Added support for AVIF images.
*
* @param string $file Full path to the file.
* @return string|false The actual mime type or false if the type cannot be determined.
Expand Down Expand Up @@ -3318,6 +3320,21 @@ function wp_get_image_mime( $file ) {
) {
$mime = 'image/webp';
}

/**
* Add AVIF fallback detection when image library doesn't support AVIF.
*
* Note: detection values come from libavif.
*
*/
if (
// ftyp.
( str_starts_with( $magic, '66747970' ) ) &&
// avif.
( 8 === strpos( $magic, '61766966' ) )
) {
$mime = 'image/avif';
}
} catch ( Exception $e ) {
$mime = false;
}
Expand Down Expand Up @@ -3357,6 +3374,7 @@ function wp_get_mime_types() {
'bmp' => 'image/bmp',
'tiff|tif' => 'image/tiff',
'webp' => 'image/webp',
'avif' => 'image/avif',
'ico' => 'image/x-icon',
'heic' => 'image/heic',
// Video formats.
Expand Down Expand Up @@ -3478,7 +3496,7 @@ function wp_get_ext_types() {
return apply_filters(
'ext2type',
array(
'image' => array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'bmp', 'tif', 'tiff', 'ico', 'heic', 'webp' ),
'image' => array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'bmp', 'tif', 'tiff', 'ico', 'heic', 'webp', 'avif' ),
'audio' => array( 'aac', 'ac3', 'aif', 'aiff', 'flac', 'm3a', 'm4a', 'm4b', 'mka', 'mp1', 'mp2', 'mp3', 'ogg', 'oga', 'ram', 'wav', 'wma' ),
'video' => array( '3g2', '3gp', '3gpp', 'asf', 'avi', 'divx', 'dv', 'flv', 'm4v', 'mkv', 'mov', 'mp4', 'mpeg', 'mpg', 'mpv', 'ogm', 'ogv', 'qt', 'rm', 'vob', 'wmv' ),
'document' => array( 'doc', 'docx', 'docm', 'dotm', 'odt', 'pages', 'pdf', 'xps', 'oxps', 'rtf', 'wp', 'wpd', 'psd', 'xcf' ),
Expand Down
94 changes: 94 additions & 0 deletions src/wp-includes/media.php
Original file line number Diff line number Diff line change
Expand Up @@ -3969,6 +3969,7 @@ function _wp_image_editor_choose( $args = array() ) {
require_once ABSPATH . WPINC . '/class-wp-image-editor.php';
require_once ABSPATH . WPINC . '/class-wp-image-editor-gd.php';
require_once ABSPATH . WPINC . '/class-wp-image-editor-imagick.php';
require_once ABSPATH . WPINC . '/class-avif-info.php';
/**
* Filters the list of image editing library classes.
*
Expand Down Expand Up @@ -4070,6 +4071,12 @@ function wp_plupload_default_settings() {
$defaults['webp_upload_error'] = true;
}


// Check if AVIF images can be edited.
if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/avif' ) ) ) {
$defaults['avif_upload_error'] = true;
}

/**
* Filters the Plupload default settings.
*
Expand Down Expand Up @@ -5335,6 +5342,7 @@ function wp_show_heic_upload_error( $plupload_settings ) {
*
* @since 5.7.0
* @since 5.8.0 Added support for WebP images.
* @since 6.4.0 Added support for AVIF images.
*
* @param string $filename The file path.
* @param array $image_info Optional. Extended image information (passed by reference).
Expand Down Expand Up @@ -5396,10 +5404,96 @@ function wp_getimagesize( $filename, array &$image_info = null ) {
}
}

// For PHP versions that don't support AVIF images,
// extract the image size info from the file headers.
if ( 'image/avif' === wp_get_image_mime( $filename ) ) {
$avif_info = wp_get_avif_info( $filename );
$width = $avif_info['width'];
$height = $avif_info['height'];

// Mimic the native return format.
if ( $width && $height ) {
return array(
$width,
$height,
IMAGETYPE_AVIF,
sprintf(
'width="%d" height="%d"',
$width,
$height
),
'mime' => 'image/avif',
);
}
}


// The image could not be parsed.
return false;
}

/**
* Extracts meta information about an AVIF file: width, height, and type.
*
* @since 6.4.0
*
* @param string $filename Path to an AVIF file.
* @return array {
* An array of AVIF image information.
*
* @type int|false $width Image width on success, false on failure.
* @type int|false $height Image height on success, false on failure.
* @type string|false $type The AVIF type: one of 'lossy' or 'lossless'. False on failure.
* }
*/
function wp_get_avif_info( $filename ) {
$width = false;
$height = false;
$type = false;

if ( 'image/avif' !== wp_get_image_mime( $filename ) ) {
return compact( 'width', 'height', 'type' );
}

if ( function_exists( 'avif_get_info' ) ) {

$avif_info = avif_get_info( $filename );

if ( ! $avif_info ) {
return compact( 'width', 'height', 'type' );
}

$width = $avif_info['width'];
$height = $avif_info['height'];
$type = $avif_info['type'];

return compact( 'width', 'height', 'type' );
} else {
// Fall back to directly parsing the file headers.
require_once ABSPATH . WPINC . '/class-avif-info.php';
$features = array(
'width' => false,
'height' => false,
'bit_depth' => false,
'num_channels' => false
);

$handle = fopen( $filename, 'rb' );
if ( $handle ) {
$parser = new Avifinfo\Parser( $handle );
$success = $parser->parse_ftyp() && $parser->parse_file();
fclose( $handle );
if ( $success ) {
$features = $parser->features->primary_item_features;
}
}

return $features;

}

}

/**
* Extracts meta information about a WebP file: width, height, and type.
*
Expand Down
Loading

0 comments on commit 341a9e3

Please sign in to comment.