wp/wp-includes/class-wp-image-editor-gd.php
changeset 18 be944660c56a
parent 16 a86126ab1dd4
child 19 3d72ae0968f4
--- a/wp/wp-includes/class-wp-image-editor-gd.php	Tue Dec 15 15:52:01 2020 +0100
+++ b/wp/wp-includes/class-wp-image-editor-gd.php	Wed Sep 21 18:19:35 2022 +0200
@@ -17,7 +17,7 @@
 	/**
 	 * GD Resource.
 	 *
-	 * @var resource
+	 * @var resource|GdImage
 	 */
 	protected $image;
 
@@ -69,6 +69,8 @@
 				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;
@@ -79,7 +81,7 @@
 	 *
 	 * @since 3.5.0
 	 *
-	 * @return bool|WP_Error True if loaded successfully; WP_Error on failure.
+	 * @return true|WP_Error True if loaded successfully; WP_Error on failure.
 	 */
 	public function load() {
 		if ( $this->image ) {
@@ -93,13 +95,28 @@
 		// Set artificially high because GD uses uncompressed images in memory.
 		wp_raise_memory_limit( 'image' );
 
-		$this->image = @imagecreatefromstring( file_get_contents( $this->file ) );
+		$file_contents = @file_get_contents( $this->file );
+
+		if ( ! $file_contents ) {
+			return new WP_Error( 'error_loading_image', __( 'File doesn’t exist?' ), $this->file );
+		}
 
-		if ( ! is_resource( $this->image ) ) {
+		// 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 );
 		}
 
-		$size = @getimagesize( $this->file );
+		$size = wp_getimagesize( $this->file );
+
 		if ( ! $size ) {
 			return new WP_Error( 'invalid_image', __( 'Could not read image size.' ), $this->file );
 		}
@@ -138,11 +155,11 @@
 
 	/**
 	 * Resizes current image.
-	 * Wraps _resize, since _resize returns a GD Resource.
+	 *
+	 * Wraps `::_resize()` which returns a GD resource or GdImage instance.
 	 *
-	 * At minimum, either a height or width must be provided.
-	 * If one of the two is set to null, the resize will
-	 * maintain aspect ratio according to the provided dimension.
+	 * At minimum, either a height or width must be provided. If one of the two is set
+	 * to null, the resize will maintain aspect ratio according to the provided dimension.
 	 *
 	 * @since 3.5.0
 	 *
@@ -158,7 +175,7 @@
 
 		$resized = $this->_resize( $max_w, $max_h, $crop );
 
-		if ( is_resource( $resized ) ) {
+		if ( is_gd_image( $resized ) ) {
 			imagedestroy( $this->image );
 			$this->image = $resized;
 			return true;
@@ -174,7 +191,7 @@
 	 * @param int        $max_w
 	 * @param int        $max_h
 	 * @param bool|array $crop
-	 * @return resource|WP_Error
+	 * @return resource|GdImage|WP_Error
 	 */
 	protected function _resize( $max_w, $max_h, $crop = false ) {
 		$dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop );
@@ -188,7 +205,7 @@
 		$resized = wp_imagecreatetruecolor( $dst_w, $dst_h );
 		imagecopyresampled( $resized, $this->image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h );
 
-		if ( is_resource( $resized ) ) {
+		if ( is_gd_image( $resized ) ) {
 			$this->update_size( $dst_w, $dst_h );
 			return $resized;
 		}
@@ -304,7 +321,7 @@
 	 * @param int  $dst_w   Optional. The destination width.
 	 * @param int  $dst_h   Optional. The destination height.
 	 * @param bool $src_abs Optional. If the source crop points are absolute.
-	 * @return bool|WP_Error
+	 * @return true|WP_Error
 	 */
 	public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) {
 		// If destination width/height isn't specified,
@@ -316,7 +333,13 @@
 			$dst_h = $src_h;
 		}
 
-		$dst = wp_imagecreatetruecolor( $dst_w, $dst_h );
+		foreach ( array( $src_w, $src_h, $dst_w, $dst_h ) as $value ) {
+			if ( ! is_numeric( $value ) || (int) $value <= 0 ) {
+				return new WP_Error( 'image_crop_error', __( 'Image crop failed.' ), $this->file );
+			}
+		}
+
+		$dst = wp_imagecreatetruecolor( (int) $dst_w, (int) $dst_h );
 
 		if ( $src_abs ) {
 			$src_w -= $src_x;
@@ -327,9 +350,9 @@
 			imageantialias( $dst, true );
 		}
 
-		imagecopyresampled( $dst, $this->image, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h );
+		imagecopyresampled( $dst, $this->image, 0, 0, (int) $src_x, (int) $src_y, (int) $dst_w, (int) $dst_h, (int) $src_w, (int) $src_h );
 
-		if ( is_resource( $dst ) ) {
+		if ( is_gd_image( $dst ) ) {
 			imagedestroy( $this->image );
 			$this->image = $dst;
 			$this->update_size();
@@ -353,7 +376,7 @@
 			$transparency = imagecolorallocatealpha( $this->image, 255, 255, 255, 127 );
 			$rotated      = imagerotate( $this->image, $angle, $transparency );
 
-			if ( is_resource( $rotated ) ) {
+			if ( is_gd_image( $rotated ) ) {
 				imagealphablending( $rotated, true );
 				imagesavealpha( $rotated, true );
 				imagedestroy( $this->image );
@@ -362,6 +385,7 @@
 				return true;
 			}
 		}
+
 		return new WP_Error( 'image_rotate_error', __( 'Image rotate failed.' ), $this->file );
 	}
 
@@ -379,7 +403,7 @@
 		$h   = $this->size['height'];
 		$dst = wp_imagecreatetruecolor( $w, $h );
 
-		if ( is_resource( $dst ) ) {
+		if ( is_gd_image( $dst ) ) {
 			$sx = $vert ? ( $w - 1 ) : 0;
 			$sy = $horz ? ( $h - 1 ) : 0;
 			$sw = $vert ? -$w : $w;
@@ -391,6 +415,7 @@
 				return true;
 			}
 		}
+
 		return new WP_Error( 'image_flip_error', __( 'Image flip failed.' ), $this->file );
 	}
 
@@ -415,9 +440,9 @@
 	}
 
 	/**
-	 * @param resource    $image
-	 * @param string|null $filename
-	 * @param string|null $mime_type
+	 * @param resource|GdImage $image
+	 * @param string|null      $filename
+	 * @param string|null      $mime_type
 	 * @return array|WP_Error
 	 */
 	protected function _save( $image, $filename = null, $mime_type = null ) {
@@ -444,6 +469,10 @@
 			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' ) );
 		}
@@ -487,6 +516,12 @@
 			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() );