mirror of
https://github.com/WordPress/WordPress.git
synced 2025-02-01 21:21:24 +01:00
Images: enable WebP support.
Add support for uploading, editing and saving WebP images when supported by the server. Add 'image/webp' to supported mime types. Correctly identify WebP images and sizes even when PHP doesn't support WebP. Resize uploaded WebP files (when supported) and use for front end markup. Props markoheijne, blobfolio, Clorith, joemcgill, atjn, desrosj, spacedmonkey, marylauc, mikeschroder, hellofromtonya, flixos90. Fixes #35725. Built from https://develop.svn.wordpress.org/trunk@50810 git-svn-id: http://core.svn.wordpress.org/trunk@50419 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
parent
e7e4b84263
commit
524030edfa
@ -306,6 +306,12 @@ function wp_stream_image( $image, $mime_type, $attachment_id ) {
|
||||
case 'image/gif':
|
||||
header( 'Content-Type: image/gif' );
|
||||
return imagegif( $image );
|
||||
case 'image/webp':
|
||||
if ( function_exists( 'imagewebp' ) ) {
|
||||
header( 'Content-Type: image/webp' );
|
||||
return imagewebp( $image, null, 90 );
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -391,6 +397,11 @@ function wp_save_image_file( $filename, $image, $mime_type, $post_id ) {
|
||||
return imagepng( $image, $filename );
|
||||
case 'image/gif':
|
||||
return imagegif( $image, $filename );
|
||||
case 'image/webp':
|
||||
if ( function_exists( 'imagewebp' ) ) {
|
||||
return imagewebp( $image, $filename );
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -517,6 +517,9 @@ function wp_generate_attachment_metadata( $attachment_id, $file ) {
|
||||
case 'image/png':
|
||||
$ext = '.png';
|
||||
break;
|
||||
case 'image/webp':
|
||||
$ext = '.webp';
|
||||
break;
|
||||
}
|
||||
$basename = str_replace( '.', '-', wp_basename( $file ) ) . '-image' . $ext;
|
||||
$uploaded = wp_upload_bits( $basename, '', $metadata['image']['data'] );
|
||||
@ -913,7 +916,7 @@ function file_is_valid_image( $path ) {
|
||||
* @return bool True if suitable, false if not suitable.
|
||||
*/
|
||||
function file_is_displayable_image( $path ) {
|
||||
$displayable_image_types = array( IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_BMP, IMAGETYPE_ICO );
|
||||
$displayable_image_types = array( IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_BMP, IMAGETYPE_ICO, IMAGETYPE_WEBP ); // phpcs:ignore PHPCompatibility.Constants.NewConstants.imagetype_webpFound
|
||||
|
||||
$info = wp_getimagesize( $path );
|
||||
if ( empty( $info ) ) {
|
||||
@ -963,6 +966,12 @@ function load_image_to_edit( $attachment_id, $mime_type, $size = 'full' ) {
|
||||
case 'image/gif':
|
||||
$image = imagecreatefromgif( $filepath );
|
||||
break;
|
||||
case 'image/webp':
|
||||
$image = false;
|
||||
if ( function_exists( 'imagecreatefromwebp' ) ) {
|
||||
$image = imagecreatefromwebp( $filepath );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$image = false;
|
||||
break;
|
||||
|
@ -993,7 +993,7 @@ function wp_media_upload_handler() {
|
||||
function media_sideload_image( $file, $post_id = 0, $desc = null, $return = 'html' ) {
|
||||
if ( ! empty( $file ) ) {
|
||||
|
||||
$allowed_extensions = array( 'jpg', 'jpeg', 'jpe', 'png', 'gif' );
|
||||
$allowed_extensions = array( 'jpg', 'jpeg', 'jpe', 'png', 'gif', 'webp' );
|
||||
|
||||
/**
|
||||
* Filters the list of allowed file extensions when sideloading an image from a URL.
|
||||
|
@ -1215,6 +1215,7 @@ We hope you enjoy your new site. Thanks!
|
||||
'jpeg',
|
||||
'png',
|
||||
'gif',
|
||||
'webp',
|
||||
// Video.
|
||||
'mov',
|
||||
'avi',
|
||||
|
@ -69,6 +69,8 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
|
||||
return ( $image_types & IMG_PNG ) != 0;
|
||||
case 'image/gif':
|
||||
return ( $image_types & IMG_GIF ) != 0;
|
||||
case 'image/webp':
|
||||
return ( $image_types & IMG_WEBP ) != 0; // phpcs:ignore PHPCompatibility.Constants.NewConstants.img_webpFound
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -99,7 +101,15 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
|
||||
return new WP_Error( 'error_loading_image', __( 'File doesn’t exist?' ), $this->file );
|
||||
}
|
||||
|
||||
$this->image = @imagecreatefromstring( $file_contents );
|
||||
// WebP may not work with imagecreatefromstring().
|
||||
if (
|
||||
function_exists( 'imagecreatefromwebp' ) &&
|
||||
( 'image/webp' === wp_get_image_mime( $this->file ) )
|
||||
) {
|
||||
$this->image = @imagecreatefromwebp( $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 );
|
||||
@ -459,6 +469,10 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
|
||||
if ( ! $this->make_image( $filename, 'imagejpeg', array( $image, $filename, $this->get_quality() ) ) ) {
|
||||
return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
|
||||
}
|
||||
} elseif ( 'image/webp' == $mime_type ) {
|
||||
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' ) );
|
||||
}
|
||||
} else {
|
||||
return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
|
||||
}
|
||||
@ -502,6 +516,12 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
|
||||
case 'image/gif':
|
||||
header( 'Content-Type: image/gif' );
|
||||
return imagegif( $this->image );
|
||||
case 'image/webp':
|
||||
if ( function_exists( 'imagewebp' ) ) {
|
||||
header( 'Content-Type: image/webp' );
|
||||
return imagewebp( $this->image, null, $this->get_quality() );
|
||||
}
|
||||
// Fall back to the default if webp isn't supported.
|
||||
default:
|
||||
header( 'Content-Type: image/jpeg' );
|
||||
return imagejpeg( $this->image, null, $this->get_quality() );
|
||||
|
@ -197,19 +197,30 @@ class WP_Image_Editor_Imagick extends WP_Image_Editor {
|
||||
}
|
||||
|
||||
try {
|
||||
if ( 'image/jpeg' === $this->mime_type ) {
|
||||
$this->image->setImageCompressionQuality( $quality );
|
||||
$this->image->setImageCompression( imagick::COMPRESSION_JPEG );
|
||||
} else {
|
||||
$this->image->setImageCompressionQuality( $quality );
|
||||
switch ( $this->mime_type ) {
|
||||
case 'image/jpeg':
|
||||
$this->image->setImageCompressionQuality( $quality );
|
||||
$this->image->setImageCompression( imagick::COMPRESSION_JPEG );
|
||||
break;
|
||||
case 'image/webp':
|
||||
if ( _wp_webp_is_lossy( $this->file ) ) {
|
||||
$this->image->setImageCompressionQuality( $quality );
|
||||
} else {
|
||||
// Use WebP lossless settings.
|
||||
$this->image->setImageCompressionQuality( 100 );
|
||||
$this->image->setOption( 'webp:lossless', 'true' );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$this->image->setImageCompressionQuality( $quality );
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
return new WP_Error( 'image_quality_error', $e->getMessage() );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets or updates current image size.
|
||||
*
|
||||
|
@ -1141,7 +1141,7 @@ final class WP_Theme implements ArrayAccess {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( array( 'png', 'gif', 'jpg', 'jpeg' ) as $ext ) {
|
||||
foreach ( array( 'png', 'gif', 'jpg', 'jpeg', 'webp' ) as $ext ) {
|
||||
if ( file_exists( $this->get_stylesheet_directory() . "/screenshot.$ext" ) ) {
|
||||
$this->cache_add( 'screenshot', 'screenshot.' . $ext );
|
||||
if ( 'relative' === $uri ) {
|
||||
|
@ -370,3 +370,11 @@ if ( ! function_exists( 'is_iterable' ) ) {
|
||||
return ( is_array( $var ) || $var instanceof Traversable );
|
||||
}
|
||||
}
|
||||
|
||||
// WebP constants may not be defined, even in cases where the format is supported.
|
||||
if ( ! defined( 'IMAGETYPE_WEBP' ) ) {
|
||||
define( 'IMAGETYPE_WEBP', 18 );
|
||||
}
|
||||
if ( ! defined( 'IMG_WEBP' ) ) {
|
||||
define( 'IMG_WEBP', IMAGETYPE_WEBP ); // phpcs:ignore PHPCompatibility.Constants.NewConstants.imagetype_webpFound
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ class WP_Customize_Media_Control extends WP_Customize_Control {
|
||||
// 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' ), true ) ? 'image' : 'document';
|
||||
$type = in_array( $ext, array( 'jpg', 'png', 'gif', 'bmp', 'webp' ), true ) ? 'image' : 'document';
|
||||
|
||||
$default_attachment = array(
|
||||
'id' => 1,
|
||||
|
@ -3340,6 +3340,8 @@ function gd_edit_image_support($mime_type) {
|
||||
return (imagetypes() & IMG_PNG) != 0;
|
||||
case 'image/gif':
|
||||
return (imagetypes() & IMG_GIF) != 0;
|
||||
case 'image/webp':
|
||||
return (imagetypes() & IMG_WEBP) != 0; // phpcs:ignore PHPCompatibility.Constants.NewConstants.img_webpFound
|
||||
}
|
||||
} else {
|
||||
switch( $mime_type ) {
|
||||
@ -3349,6 +3351,8 @@ function gd_edit_image_support($mime_type) {
|
||||
return function_exists('imagecreatefrompng');
|
||||
case 'image/gif':
|
||||
return function_exists('imagecreatefromgif');
|
||||
case 'image/webp':
|
||||
return function_exists('imagecreatefromwebp');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -3318,7 +3318,7 @@ function translate_smiley( $matches ) {
|
||||
|
||||
$matches = array();
|
||||
$ext = preg_match( '/\.([^.]+)$/', $img, $matches ) ? strtolower( $matches[1] ) : false;
|
||||
$image_exts = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png' );
|
||||
$image_exts = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'webp' );
|
||||
|
||||
// Don't convert smilies that aren't images - they're probably emoji.
|
||||
if ( ! in_array( $ext, $image_exts, true ) ) {
|
||||
|
@ -2886,6 +2886,7 @@ function wp_check_filetype_and_ext( $file, $filename, $mimes = null ) {
|
||||
'image/gif' => 'gif',
|
||||
'image/bmp' => 'bmp',
|
||||
'image/tiff' => 'tif',
|
||||
'image/webp' => 'webp',
|
||||
)
|
||||
);
|
||||
|
||||
@ -3063,6 +3064,35 @@ function wp_get_image_mime( $file ) {
|
||||
} else {
|
||||
$mime = false;
|
||||
}
|
||||
|
||||
if ( false !== $mime ) {
|
||||
return $mime;
|
||||
}
|
||||
|
||||
$handle = fopen( $file, 'rb' );
|
||||
if ( false === $handle ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$magic = fread( $handle, 12 );
|
||||
if ( false === $magic ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add WebP fallback detection when image library doesn't support WebP.
|
||||
// Note: detection values come from LibWebP, see
|
||||
// https://github.com/webmproject/libwebp/blob/master/imageio/image_dec.c#L30
|
||||
$magic = bin2hex( $magic );
|
||||
if (
|
||||
// RIFF.
|
||||
( 0 === strpos( $magic, '52494646' ) ) &&
|
||||
// WEBP.
|
||||
( 16 === strpos( $magic, '57454250' ) )
|
||||
) {
|
||||
$mime = 'image/webp';
|
||||
}
|
||||
|
||||
fclose( $handle );
|
||||
} catch ( Exception $e ) {
|
||||
$mime = false;
|
||||
}
|
||||
@ -3101,6 +3131,7 @@ function wp_get_mime_types() {
|
||||
'png' => 'image/png',
|
||||
'bmp' => 'image/bmp',
|
||||
'tiff|tif' => 'image/tiff',
|
||||
'webp' => 'image/webp',
|
||||
'ico' => 'image/x-icon',
|
||||
'heic' => 'image/heic',
|
||||
// Video formats.
|
||||
@ -3222,7 +3253,7 @@ function wp_get_ext_types() {
|
||||
return apply_filters(
|
||||
'ext2type',
|
||||
array(
|
||||
'image' => array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'bmp', 'tif', 'tiff', 'ico', 'heic' ),
|
||||
'image' => array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'bmp', 'tif', 'tiff', 'ico', 'heic', 'webp' ),
|
||||
'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' ),
|
||||
|
@ -4980,6 +4980,7 @@ function wp_show_heic_upload_error( $plupload_settings ) {
|
||||
* Allows PHP's getimagesize() to be debuggable when necessary.
|
||||
*
|
||||
* @since 5.7.0
|
||||
* @since 5.8.0 Added support for WebP images.
|
||||
*
|
||||
* @param string $filename The file path.
|
||||
* @param array $image_info Optional. Extended image information (passed by reference).
|
||||
@ -4994,9 +4995,9 @@ function wp_getimagesize( $filename, array &$image_info = null ) {
|
||||
defined( 'WP_DEBUG' ) && WP_DEBUG
|
||||
) {
|
||||
if ( 2 === func_num_args() ) {
|
||||
return getimagesize( $filename, $image_info );
|
||||
return _wp_get_image_size( $filename, $image_info );
|
||||
} else {
|
||||
return getimagesize( $filename );
|
||||
return _wp_get_image_size( $filename );
|
||||
}
|
||||
}
|
||||
|
||||
@ -5011,9 +5012,133 @@ function wp_getimagesize( $filename, array &$image_info = null ) {
|
||||
*/
|
||||
if ( 2 === func_num_args() ) {
|
||||
// phpcs:ignore WordPress.PHP.NoSilencedErrors
|
||||
return @getimagesize( $filename, $image_info );
|
||||
return @_wp_get_image_size( $filename, $image_info );
|
||||
} else {
|
||||
// phpcs:ignore WordPress.PHP.NoSilencedErrors
|
||||
return @getimagesize( $filename );
|
||||
return @_wp_get_image_size( $filename );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts meta information about a webp file: width, height and type.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param [type] $filename Path to a WebP file.
|
||||
* @return array $webp_info {
|
||||
* An array of WebP image information.
|
||||
*
|
||||
* @type array $size {
|
||||
* @type int $width Image width.
|
||||
* @type int $height Image height.
|
||||
* @type bool $type The WebP type: one of 'lossy', 'lossless' or 'animated-alpha'.
|
||||
* }
|
||||
*/
|
||||
function wp_get_webp_info( $filename ) {
|
||||
$width = false;
|
||||
$height = false;
|
||||
$type = false;
|
||||
if ( ! 'image/webp' === wp_get_image_mime( $filename ) ) {
|
||||
return compact( 'width', 'height', 'type' );
|
||||
}
|
||||
try {
|
||||
$handle = fopen( $filename, 'rb' );
|
||||
if ( $handle ) {
|
||||
$magic = fread( $handle, 40 );
|
||||
fclose( $handle );
|
||||
|
||||
// Make sure we got enough bytes.
|
||||
if ( strlen( $magic ) < 40 ) {
|
||||
return compact( 'width', 'height', 'type' );
|
||||
}
|
||||
|
||||
// The headers are a little different for each of the three formats.
|
||||
// Header values based on WebP docs, see https://developers.google.com/speed/webp/docs/riff_container.
|
||||
switch ( substr( $magic, 12, 4 ) ) {
|
||||
// Lossy WebP.
|
||||
case 'VP8 ':
|
||||
$parts = unpack( 'v2', substr( $magic, 26, 4 ) );
|
||||
$width = (int) ( $parts[1] & 0x3FFF );
|
||||
$height = (int) ( $parts[2] & 0x3FFF );
|
||||
$type = 'lossy';
|
||||
break;
|
||||
// Lossless WebP.
|
||||
case 'VP8L':
|
||||
$parts = unpack( 'C4', substr( $magic, 21, 4 ) );
|
||||
$width = (int) ( $parts[1] | ( ( $parts[2] & 0x3F ) << 8 ) ) + 1;
|
||||
$height = (int) ( ( ( $parts[2] & 0xC0 ) >> 6 ) | ( $parts[3] << 2 ) | ( ( $parts[4] & 0x03 ) << 10 ) ) + 1;
|
||||
$type = 'lossless';
|
||||
break;
|
||||
// Animated/alpha WebP.
|
||||
case 'VP8X':
|
||||
// Pad 24-bit int.
|
||||
$width = unpack( 'V', substr( $magic, 24, 3 ) . "\x00" );
|
||||
$width = (int) ( $width[1] & 0xFFFFFF ) + 1;
|
||||
// Pad 24-bit int.
|
||||
$height = unpack( 'V', substr( $magic, 27, 3 ) . "\x00" );
|
||||
$height = (int) ( $height[1] & 0xFFFFFF ) + 1;
|
||||
$type = 'animated-alpha';
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
}
|
||||
return compact( 'width', 'height', 'type' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a passed image is a lossy WebP image.
|
||||
*
|
||||
* @since 5.8.0
|
||||
*
|
||||
* @param string $filename The file path.
|
||||
* @return bool Whether the file is a lossy WebP file.
|
||||
*/
|
||||
function _wp_webp_is_lossy( $filename ) {
|
||||
$webp_info = wp_get_webp_info( $filename );
|
||||
$type = $webp_info['type'];
|
||||
return $type && 'lossy' === $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the image size, with support for WebP images.
|
||||
*
|
||||
* @since 5.8.0
|
||||
* @access private
|
||||
*
|
||||
* @param string $filename The file path.
|
||||
* @param array $imageinfo Extended image information, passed by reference.
|
||||
* @return array|false Array of image information or false on failure.
|
||||
*/
|
||||
function _wp_get_image_size( $filename, &$imageinfo = array() ) {
|
||||
// Try getimagesize() first.
|
||||
$info = getimagesize( $filename, $imageinfo );
|
||||
if ( false !== $info ) {
|
||||
return $info;
|
||||
}
|
||||
// For PHP versions that don't support WebP images, extract the image
|
||||
// size info from the file headers.
|
||||
if ( 'image/webp' === wp_get_image_mime( $filename ) ) {
|
||||
$webp_info = wp_get_webp_info( $filename );
|
||||
$width = $webp_info['width'];
|
||||
$height = $webp_info['height'];
|
||||
|
||||
// Mimic the native return format.
|
||||
if ( $width && $height ) {
|
||||
return array(
|
||||
$width,
|
||||
$height,
|
||||
IMAGETYPE_WEBP, // phpcs:ignore PHPCompatibility.Constants.NewConstants.imagetype_webpFound
|
||||
sprintf(
|
||||
'width="%d" height="%d"',
|
||||
$width,
|
||||
$height
|
||||
),
|
||||
'mime' => 'image/webp',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// The image could not be parsed.
|
||||
return false;
|
||||
}
|
||||
|
@ -6539,7 +6539,7 @@ function wp_attachment_is( $type, $post = null ) {
|
||||
|
||||
switch ( $type ) {
|
||||
case 'image':
|
||||
$image_exts = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png' );
|
||||
$image_exts = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'webp' );
|
||||
return in_array( $ext, $image_exts, true );
|
||||
|
||||
case 'audio':
|
||||
|
@ -438,7 +438,7 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
|
||||
);
|
||||
}
|
||||
|
||||
$supported_types = array( 'image/jpeg', 'image/png', 'image/gif' );
|
||||
$supported_types = array( 'image/jpeg', 'image/png', 'image/gif', 'image/webp' );
|
||||
$mime_type = get_post_mime_type( $attachment_id );
|
||||
if ( ! in_array( $mime_type, $supported_types, true ) ) {
|
||||
return new WP_Error(
|
||||
|
@ -13,7 +13,7 @@
|
||||
*
|
||||
* @global string $wp_version
|
||||
*/
|
||||
$wp_version = '5.8-alpha-50809';
|
||||
$wp_version = '5.8-alpha-50810';
|
||||
|
||||
/**
|
||||
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
|
||||
|
Loading…
Reference in New Issue
Block a user