wp/wp-includes/class-wp-image-editor-gd.php
changeset 18 be944660c56a
parent 16 a86126ab1dd4
child 19 3d72ae0968f4
equal deleted inserted replaced
17:34716fd837a4 18:be944660c56a
    15  */
    15  */
    16 class WP_Image_Editor_GD extends WP_Image_Editor {
    16 class WP_Image_Editor_GD extends WP_Image_Editor {
    17 	/**
    17 	/**
    18 	 * GD Resource.
    18 	 * GD Resource.
    19 	 *
    19 	 *
    20 	 * @var resource
    20 	 * @var resource|GdImage
    21 	 */
    21 	 */
    22 	protected $image;
    22 	protected $image;
    23 
    23 
    24 	public function __destruct() {
    24 	public function __destruct() {
    25 		if ( $this->image ) {
    25 		if ( $this->image ) {
    67 				return ( $image_types & IMG_JPG ) != 0;
    67 				return ( $image_types & IMG_JPG ) != 0;
    68 			case 'image/png':
    68 			case 'image/png':
    69 				return ( $image_types & IMG_PNG ) != 0;
    69 				return ( $image_types & IMG_PNG ) != 0;
    70 			case 'image/gif':
    70 			case 'image/gif':
    71 				return ( $image_types & IMG_GIF ) != 0;
    71 				return ( $image_types & IMG_GIF ) != 0;
       
    72 			case 'image/webp':
       
    73 				return ( $image_types & IMG_WEBP ) != 0; // phpcs:ignore PHPCompatibility.Constants.NewConstants.img_webpFound
    72 		}
    74 		}
    73 
    75 
    74 		return false;
    76 		return false;
    75 	}
    77 	}
    76 
    78 
    77 	/**
    79 	/**
    78 	 * Loads image from $this->file into new GD Resource.
    80 	 * Loads image from $this->file into new GD Resource.
    79 	 *
    81 	 *
    80 	 * @since 3.5.0
    82 	 * @since 3.5.0
    81 	 *
    83 	 *
    82 	 * @return bool|WP_Error True if loaded successfully; WP_Error on failure.
    84 	 * @return true|WP_Error True if loaded successfully; WP_Error on failure.
    83 	 */
    85 	 */
    84 	public function load() {
    86 	public function load() {
    85 		if ( $this->image ) {
    87 		if ( $this->image ) {
    86 			return true;
    88 			return true;
    87 		}
    89 		}
    91 		}
    93 		}
    92 
    94 
    93 		// Set artificially high because GD uses uncompressed images in memory.
    95 		// Set artificially high because GD uses uncompressed images in memory.
    94 		wp_raise_memory_limit( 'image' );
    96 		wp_raise_memory_limit( 'image' );
    95 
    97 
    96 		$this->image = @imagecreatefromstring( file_get_contents( $this->file ) );
    98 		$file_contents = @file_get_contents( $this->file );
    97 
    99 
    98 		if ( ! is_resource( $this->image ) ) {
   100 		if ( ! $file_contents ) {
       
   101 			return new WP_Error( 'error_loading_image', __( 'File doesn’t exist?' ), $this->file );
       
   102 		}
       
   103 
       
   104 		// WebP may not work with imagecreatefromstring().
       
   105 		if (
       
   106 			function_exists( 'imagecreatefromwebp' ) &&
       
   107 			( 'image/webp' === wp_get_image_mime( $this->file ) )
       
   108 		) {
       
   109 			$this->image = @imagecreatefromwebp( $this->file );
       
   110 		} else {
       
   111 			$this->image = @imagecreatefromstring( $file_contents );
       
   112 		}
       
   113 
       
   114 		if ( ! is_gd_image( $this->image ) ) {
    99 			return new WP_Error( 'invalid_image', __( 'File is not an image.' ), $this->file );
   115 			return new WP_Error( 'invalid_image', __( 'File is not an image.' ), $this->file );
   100 		}
   116 		}
   101 
   117 
   102 		$size = @getimagesize( $this->file );
   118 		$size = wp_getimagesize( $this->file );
       
   119 
   103 		if ( ! $size ) {
   120 		if ( ! $size ) {
   104 			return new WP_Error( 'invalid_image', __( 'Could not read image size.' ), $this->file );
   121 			return new WP_Error( 'invalid_image', __( 'Could not read image size.' ), $this->file );
   105 		}
   122 		}
   106 
   123 
   107 		if ( function_exists( 'imagealphablending' ) && function_exists( 'imagesavealpha' ) ) {
   124 		if ( function_exists( 'imagealphablending' ) && function_exists( 'imagesavealpha' ) ) {
   136 		return parent::update_size( $width, $height );
   153 		return parent::update_size( $width, $height );
   137 	}
   154 	}
   138 
   155 
   139 	/**
   156 	/**
   140 	 * Resizes current image.
   157 	 * Resizes current image.
   141 	 * Wraps _resize, since _resize returns a GD Resource.
   158 	 *
   142 	 *
   159 	 * Wraps `::_resize()` which returns a GD resource or GdImage instance.
   143 	 * At minimum, either a height or width must be provided.
   160 	 *
   144 	 * If one of the two is set to null, the resize will
   161 	 * At minimum, either a height or width must be provided. If one of the two is set
   145 	 * maintain aspect ratio according to the provided dimension.
   162 	 * to null, the resize will maintain aspect ratio according to the provided dimension.
   146 	 *
   163 	 *
   147 	 * @since 3.5.0
   164 	 * @since 3.5.0
   148 	 *
   165 	 *
   149 	 * @param int|null $max_w Image width.
   166 	 * @param int|null $max_w Image width.
   150 	 * @param int|null $max_h Image height.
   167 	 * @param int|null $max_h Image height.
   156 			return true;
   173 			return true;
   157 		}
   174 		}
   158 
   175 
   159 		$resized = $this->_resize( $max_w, $max_h, $crop );
   176 		$resized = $this->_resize( $max_w, $max_h, $crop );
   160 
   177 
   161 		if ( is_resource( $resized ) ) {
   178 		if ( is_gd_image( $resized ) ) {
   162 			imagedestroy( $this->image );
   179 			imagedestroy( $this->image );
   163 			$this->image = $resized;
   180 			$this->image = $resized;
   164 			return true;
   181 			return true;
   165 
   182 
   166 		} elseif ( is_wp_error( $resized ) ) {
   183 		} elseif ( is_wp_error( $resized ) ) {
   172 
   189 
   173 	/**
   190 	/**
   174 	 * @param int        $max_w
   191 	 * @param int        $max_w
   175 	 * @param int        $max_h
   192 	 * @param int        $max_h
   176 	 * @param bool|array $crop
   193 	 * @param bool|array $crop
   177 	 * @return resource|WP_Error
   194 	 * @return resource|GdImage|WP_Error
   178 	 */
   195 	 */
   179 	protected function _resize( $max_w, $max_h, $crop = false ) {
   196 	protected function _resize( $max_w, $max_h, $crop = false ) {
   180 		$dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop );
   197 		$dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop );
   181 
   198 
   182 		if ( ! $dims ) {
   199 		if ( ! $dims ) {
   186 		list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims;
   203 		list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims;
   187 
   204 
   188 		$resized = wp_imagecreatetruecolor( $dst_w, $dst_h );
   205 		$resized = wp_imagecreatetruecolor( $dst_w, $dst_h );
   189 		imagecopyresampled( $resized, $this->image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h );
   206 		imagecopyresampled( $resized, $this->image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h );
   190 
   207 
   191 		if ( is_resource( $resized ) ) {
   208 		if ( is_gd_image( $resized ) ) {
   192 			$this->update_size( $dst_w, $dst_h );
   209 			$this->update_size( $dst_w, $dst_h );
   193 			return $resized;
   210 			return $resized;
   194 		}
   211 		}
   195 
   212 
   196 		return new WP_Error( 'image_resize_error', __( 'Image resize failed.' ), $this->file );
   213 		return new WP_Error( 'image_resize_error', __( 'Image resize failed.' ), $this->file );
   302 	 * @param int  $src_w   The width to crop.
   319 	 * @param int  $src_w   The width to crop.
   303 	 * @param int  $src_h   The height to crop.
   320 	 * @param int  $src_h   The height to crop.
   304 	 * @param int  $dst_w   Optional. The destination width.
   321 	 * @param int  $dst_w   Optional. The destination width.
   305 	 * @param int  $dst_h   Optional. The destination height.
   322 	 * @param int  $dst_h   Optional. The destination height.
   306 	 * @param bool $src_abs Optional. If the source crop points are absolute.
   323 	 * @param bool $src_abs Optional. If the source crop points are absolute.
   307 	 * @return bool|WP_Error
   324 	 * @return true|WP_Error
   308 	 */
   325 	 */
   309 	public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) {
   326 	public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) {
   310 		// If destination width/height isn't specified,
   327 		// If destination width/height isn't specified,
   311 		// use same as width/height from source.
   328 		// use same as width/height from source.
   312 		if ( ! $dst_w ) {
   329 		if ( ! $dst_w ) {
   314 		}
   331 		}
   315 		if ( ! $dst_h ) {
   332 		if ( ! $dst_h ) {
   316 			$dst_h = $src_h;
   333 			$dst_h = $src_h;
   317 		}
   334 		}
   318 
   335 
   319 		$dst = wp_imagecreatetruecolor( $dst_w, $dst_h );
   336 		foreach ( array( $src_w, $src_h, $dst_w, $dst_h ) as $value ) {
       
   337 			if ( ! is_numeric( $value ) || (int) $value <= 0 ) {
       
   338 				return new WP_Error( 'image_crop_error', __( 'Image crop failed.' ), $this->file );
       
   339 			}
       
   340 		}
       
   341 
       
   342 		$dst = wp_imagecreatetruecolor( (int) $dst_w, (int) $dst_h );
   320 
   343 
   321 		if ( $src_abs ) {
   344 		if ( $src_abs ) {
   322 			$src_w -= $src_x;
   345 			$src_w -= $src_x;
   323 			$src_h -= $src_y;
   346 			$src_h -= $src_y;
   324 		}
   347 		}
   325 
   348 
   326 		if ( function_exists( 'imageantialias' ) ) {
   349 		if ( function_exists( 'imageantialias' ) ) {
   327 			imageantialias( $dst, true );
   350 			imageantialias( $dst, true );
   328 		}
   351 		}
   329 
   352 
   330 		imagecopyresampled( $dst, $this->image, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h );
   353 		imagecopyresampled( $dst, $this->image, 0, 0, (int) $src_x, (int) $src_y, (int) $dst_w, (int) $dst_h, (int) $src_w, (int) $src_h );
   331 
   354 
   332 		if ( is_resource( $dst ) ) {
   355 		if ( is_gd_image( $dst ) ) {
   333 			imagedestroy( $this->image );
   356 			imagedestroy( $this->image );
   334 			$this->image = $dst;
   357 			$this->image = $dst;
   335 			$this->update_size();
   358 			$this->update_size();
   336 			return true;
   359 			return true;
   337 		}
   360 		}
   351 	public function rotate( $angle ) {
   374 	public function rotate( $angle ) {
   352 		if ( function_exists( 'imagerotate' ) ) {
   375 		if ( function_exists( 'imagerotate' ) ) {
   353 			$transparency = imagecolorallocatealpha( $this->image, 255, 255, 255, 127 );
   376 			$transparency = imagecolorallocatealpha( $this->image, 255, 255, 255, 127 );
   354 			$rotated      = imagerotate( $this->image, $angle, $transparency );
   377 			$rotated      = imagerotate( $this->image, $angle, $transparency );
   355 
   378 
   356 			if ( is_resource( $rotated ) ) {
   379 			if ( is_gd_image( $rotated ) ) {
   357 				imagealphablending( $rotated, true );
   380 				imagealphablending( $rotated, true );
   358 				imagesavealpha( $rotated, true );
   381 				imagesavealpha( $rotated, true );
   359 				imagedestroy( $this->image );
   382 				imagedestroy( $this->image );
   360 				$this->image = $rotated;
   383 				$this->image = $rotated;
   361 				$this->update_size();
   384 				$this->update_size();
   362 				return true;
   385 				return true;
   363 			}
   386 			}
   364 		}
   387 		}
       
   388 
   365 		return new WP_Error( 'image_rotate_error', __( 'Image rotate failed.' ), $this->file );
   389 		return new WP_Error( 'image_rotate_error', __( 'Image rotate failed.' ), $this->file );
   366 	}
   390 	}
   367 
   391 
   368 	/**
   392 	/**
   369 	 * Flips current image.
   393 	 * Flips current image.
   377 	public function flip( $horz, $vert ) {
   401 	public function flip( $horz, $vert ) {
   378 		$w   = $this->size['width'];
   402 		$w   = $this->size['width'];
   379 		$h   = $this->size['height'];
   403 		$h   = $this->size['height'];
   380 		$dst = wp_imagecreatetruecolor( $w, $h );
   404 		$dst = wp_imagecreatetruecolor( $w, $h );
   381 
   405 
   382 		if ( is_resource( $dst ) ) {
   406 		if ( is_gd_image( $dst ) ) {
   383 			$sx = $vert ? ( $w - 1 ) : 0;
   407 			$sx = $vert ? ( $w - 1 ) : 0;
   384 			$sy = $horz ? ( $h - 1 ) : 0;
   408 			$sy = $horz ? ( $h - 1 ) : 0;
   385 			$sw = $vert ? -$w : $w;
   409 			$sw = $vert ? -$w : $w;
   386 			$sh = $horz ? -$h : $h;
   410 			$sh = $horz ? -$h : $h;
   387 
   411 
   389 				imagedestroy( $this->image );
   413 				imagedestroy( $this->image );
   390 				$this->image = $dst;
   414 				$this->image = $dst;
   391 				return true;
   415 				return true;
   392 			}
   416 			}
   393 		}
   417 		}
       
   418 
   394 		return new WP_Error( 'image_flip_error', __( 'Image flip failed.' ), $this->file );
   419 		return new WP_Error( 'image_flip_error', __( 'Image flip failed.' ), $this->file );
   395 	}
   420 	}
   396 
   421 
   397 	/**
   422 	/**
   398 	 * Saves current in-memory image to file.
   423 	 * Saves current in-memory image to file.
   413 
   438 
   414 		return $saved;
   439 		return $saved;
   415 	}
   440 	}
   416 
   441 
   417 	/**
   442 	/**
   418 	 * @param resource    $image
   443 	 * @param resource|GdImage $image
   419 	 * @param string|null $filename
   444 	 * @param string|null      $filename
   420 	 * @param string|null $mime_type
   445 	 * @param string|null      $mime_type
   421 	 * @return array|WP_Error
   446 	 * @return array|WP_Error
   422 	 */
   447 	 */
   423 	protected function _save( $image, $filename = null, $mime_type = null ) {
   448 	protected function _save( $image, $filename = null, $mime_type = null ) {
   424 		list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
   449 		list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
   425 
   450 
   440 			if ( ! $this->make_image( $filename, 'imagepng', array( $image, $filename ) ) ) {
   465 			if ( ! $this->make_image( $filename, 'imagepng', array( $image, $filename ) ) ) {
   441 				return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
   466 				return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
   442 			}
   467 			}
   443 		} elseif ( 'image/jpeg' === $mime_type ) {
   468 		} elseif ( 'image/jpeg' === $mime_type ) {
   444 			if ( ! $this->make_image( $filename, 'imagejpeg', array( $image, $filename, $this->get_quality() ) ) ) {
   469 			if ( ! $this->make_image( $filename, 'imagejpeg', array( $image, $filename, $this->get_quality() ) ) ) {
       
   470 				return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
       
   471 			}
       
   472 		} elseif ( 'image/webp' == $mime_type ) {
       
   473 			if ( ! function_exists( 'imagewebp' ) || ! $this->make_image( $filename, 'imagewebp', array( $image, $filename, $this->get_quality() ) ) ) {
   445 				return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
   474 				return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
   446 			}
   475 			}
   447 		} else {
   476 		} else {
   448 			return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
   477 			return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
   449 		}
   478 		}
   485 				header( 'Content-Type: image/png' );
   514 				header( 'Content-Type: image/png' );
   486 				return imagepng( $this->image );
   515 				return imagepng( $this->image );
   487 			case 'image/gif':
   516 			case 'image/gif':
   488 				header( 'Content-Type: image/gif' );
   517 				header( 'Content-Type: image/gif' );
   489 				return imagegif( $this->image );
   518 				return imagegif( $this->image );
       
   519 			case 'image/webp':
       
   520 				if ( function_exists( 'imagewebp' ) ) {
       
   521 					header( 'Content-Type: image/webp' );
       
   522 					return imagewebp( $this->image, null, $this->get_quality() );
       
   523 				}
       
   524 				// Fall back to the default if webp isn't supported.
   490 			default:
   525 			default:
   491 				header( 'Content-Type: image/jpeg' );
   526 				header( 'Content-Type: image/jpeg' );
   492 				return imagejpeg( $this->image, null, $this->get_quality() );
   527 				return imagejpeg( $this->image, null, $this->get_quality() );
   493 		}
   528 		}
   494 	}
   529 	}