wp/wp-includes/class-wp-image-editor-gd.php
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
    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 ) {
    26 			// we don't need the original in memory anymore
    26 			// We don't need the original in memory anymore.
    27 			imagedestroy( $this->image );
    27 			imagedestroy( $this->image );
    28 		}
    28 		}
    29 	}
    29 	}
    30 
    30 
    31 	/**
    31 	/**
    39 	public static function test( $args = array() ) {
    39 	public static function test( $args = array() ) {
    40 		if ( ! extension_loaded( 'gd' ) || ! function_exists( 'gd_info' ) ) {
    40 		if ( ! extension_loaded( 'gd' ) || ! function_exists( 'gd_info' ) ) {
    41 			return false;
    41 			return false;
    42 		}
    42 		}
    43 
    43 
    44 		// On some setups GD library does not provide imagerotate() - Ticket #11536
    44 		// On some setups GD library does not provide imagerotate() - Ticket #11536.
    45 		if ( isset( $args['methods'] ) &&
    45 		if ( isset( $args['methods'] ) &&
    46 			in_array( 'rotate', $args['methods'] ) &&
    46 			in_array( 'rotate', $args['methods'], true ) &&
    47 			! function_exists( 'imagerotate' ) ) {
    47 			! function_exists( 'imagerotate' ) ) {
    48 
    48 
    49 				return false;
    49 				return false;
    50 		}
    50 		}
    51 
    51 
   144 	 * If one of the two is set to null, the resize will
   144 	 * If one of the two is set to null, the resize will
   145 	 * maintain aspect ratio according to the provided dimension.
   145 	 * maintain aspect ratio according to the provided dimension.
   146 	 *
   146 	 *
   147 	 * @since 3.5.0
   147 	 * @since 3.5.0
   148 	 *
   148 	 *
   149 	 * @param  int|null $max_w Image width.
   149 	 * @param int|null $max_w Image width.
   150 	 * @param  int|null $max_h Image height.
   150 	 * @param int|null $max_h Image height.
   151 	 * @param  bool     $crop
   151 	 * @param bool     $crop
   152 	 * @return true|WP_Error
   152 	 * @return true|WP_Error
   153 	 */
   153 	 */
   154 	public function resize( $max_w, $max_h, $crop = false ) {
   154 	public function resize( $max_w, $max_h, $crop = false ) {
   155 		if ( ( $this->size['width'] == $max_w ) && ( $this->size['height'] == $max_h ) ) {
   155 		if ( ( $this->size['width'] == $max_w ) && ( $this->size['height'] == $max_h ) ) {
   156 			return true;
   156 			return true;
   169 
   169 
   170 		return new WP_Error( 'image_resize_error', __( 'Image resize failed.' ), $this->file );
   170 		return new WP_Error( 'image_resize_error', __( 'Image resize failed.' ), $this->file );
   171 	}
   171 	}
   172 
   172 
   173 	/**
   173 	/**
   174 	 * @param int $max_w
   174 	 * @param int        $max_w
   175 	 * @param int $max_h
   175 	 * @param int        $max_h
   176 	 * @param bool|array $crop
   176 	 * @param bool|array $crop
   177 	 * @return resource|WP_Error
   177 	 * @return resource|WP_Error
   178 	 */
   178 	 */
   179 	protected function _resize( $max_w, $max_h, $crop = false ) {
   179 	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 );
   180 		$dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop );
       
   181 
   181 		if ( ! $dims ) {
   182 		if ( ! $dims ) {
   182 			return new WP_Error( 'error_getting_dimensions', __( 'Could not calculate resized image dimensions' ), $this->file );
   183 			return new WP_Error( 'error_getting_dimensions', __( 'Could not calculate resized image dimensions' ), $this->file );
   183 		}
   184 		}
       
   185 
   184 		list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims;
   186 		list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims;
   185 
   187 
   186 		$resized = wp_imagecreatetruecolor( $dst_w, $dst_h );
   188 		$resized = wp_imagecreatetruecolor( $dst_w, $dst_h );
   187 		imagecopyresampled( $resized, $this->image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h );
   189 		imagecopyresampled( $resized, $this->image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h );
   188 
   190 
   193 
   195 
   194 		return new WP_Error( 'image_resize_error', __( 'Image resize failed.' ), $this->file );
   196 		return new WP_Error( 'image_resize_error', __( 'Image resize failed.' ), $this->file );
   195 	}
   197 	}
   196 
   198 
   197 	/**
   199 	/**
   198 	 * Resize multiple images from a single source.
   200 	 * Create multiple smaller images from a single source.
       
   201 	 *
       
   202 	 * Attempts to create all sub-sizes and returns the meta data at the end. This
       
   203 	 * may result in the server running out of resources. When it fails there may be few
       
   204 	 * "orphaned" images left over as the meta data is never returned and saved.
       
   205 	 *
       
   206 	 * As of 5.3.0 the preferred way to do this is with `make_subsize()`. It creates
       
   207 	 * the new images one at a time and allows for the meta data to be saved after
       
   208 	 * each new image is created.
   199 	 *
   209 	 *
   200 	 * @since 3.5.0
   210 	 * @since 3.5.0
   201 	 *
   211 	 *
   202 	 * @param array $sizes {
   212 	 * @param array $sizes {
   203 	 *     An array of image size arrays. Default sizes are 'small', 'medium', 'medium_large', 'large'.
   213 	 *     An array of image size data arrays.
   204 	 *
   214 	 *
   205 	 *     Either a height or width must be provided.
   215 	 *     Either a height or width must be provided.
   206 	 *     If one of the two is set to null, the resize will
   216 	 *     If one of the two is set to null, the resize will
   207 	 *     maintain aspect ratio according to the provided dimension.
   217 	 *     maintain aspect ratio according to the source image.
   208 	 *
   218 	 *
   209 	 *     @type array $size {
   219 	 *     @type array $size {
   210 	 *         Array of height, width values, and whether to crop.
   220 	 *         Array of height, width values, and whether to crop.
   211 	 *
   221 	 *
   212 	 *         @type int  $width  Image width. Optional if `$height` is specified.
   222 	 *         @type int  $width  Image width. Optional if `$height` is specified.
   215 	 *     }
   225 	 *     }
   216 	 * }
   226 	 * }
   217 	 * @return array An array of resized images' metadata by size.
   227 	 * @return array An array of resized images' metadata by size.
   218 	 */
   228 	 */
   219 	public function multi_resize( $sizes ) {
   229 	public function multi_resize( $sizes ) {
   220 		$metadata  = array();
   230 		$metadata = array();
       
   231 
       
   232 		foreach ( $sizes as $size => $size_data ) {
       
   233 			$meta = $this->make_subsize( $size_data );
       
   234 
       
   235 			if ( ! is_wp_error( $meta ) ) {
       
   236 				$metadata[ $size ] = $meta;
       
   237 			}
       
   238 		}
       
   239 
       
   240 		return $metadata;
       
   241 	}
       
   242 
       
   243 	/**
       
   244 	 * Create an image sub-size and return the image meta data value for it.
       
   245 	 *
       
   246 	 * @since 5.3.0
       
   247 	 *
       
   248 	 * @param array $size_data {
       
   249 	 *     Array of size data.
       
   250 	 *
       
   251 	 *     @type int  $width  The maximum width in pixels.
       
   252 	 *     @type int  $height The maximum height in pixels.
       
   253 	 *     @type bool $crop   Whether to crop the image to exact dimensions.
       
   254 	 * }
       
   255 	 * @return array|WP_Error The image data array for inclusion in the `sizes` array in the image meta,
       
   256 	 *                        WP_Error object on error.
       
   257 	 */
       
   258 	public function make_subsize( $size_data ) {
       
   259 		if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) {
       
   260 			return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) );
       
   261 		}
       
   262 
   221 		$orig_size = $this->size;
   263 		$orig_size = $this->size;
   222 
   264 
   223 		foreach ( $sizes as $size => $size_data ) {
   265 		if ( ! isset( $size_data['width'] ) ) {
   224 			if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) {
   266 			$size_data['width'] = null;
   225 				continue;
   267 		}
   226 			}
   268 
   227 
   269 		if ( ! isset( $size_data['height'] ) ) {
   228 			if ( ! isset( $size_data['width'] ) ) {
   270 			$size_data['height'] = null;
   229 				$size_data['width'] = null;
   271 		}
   230 			}
   272 
   231 			if ( ! isset( $size_data['height'] ) ) {
   273 		if ( ! isset( $size_data['crop'] ) ) {
   232 				$size_data['height'] = null;
   274 			$size_data['crop'] = false;
   233 			}
   275 		}
   234 
   276 
   235 			if ( ! isset( $size_data['crop'] ) ) {
   277 		$resized = $this->_resize( $size_data['width'], $size_data['height'], $size_data['crop'] );
   236 				$size_data['crop'] = false;
   278 
   237 			}
   279 		if ( is_wp_error( $resized ) ) {
   238 
   280 			$saved = $resized;
   239 			$image     = $this->_resize( $size_data['width'], $size_data['height'], $size_data['crop'] );
   281 		} else {
   240 			$duplicate = ( ( $orig_size['width'] == $size_data['width'] ) && ( $orig_size['height'] == $size_data['height'] ) );
   282 			$saved = $this->_save( $resized );
   241 
   283 			imagedestroy( $resized );
   242 			if ( ! is_wp_error( $image ) && ! $duplicate ) {
   284 		}
   243 				$resized = $this->_save( $image );
   285 
   244 
   286 		$this->size = $orig_size;
   245 				imagedestroy( $image );
   287 
   246 
   288 		if ( ! is_wp_error( $saved ) ) {
   247 				if ( ! is_wp_error( $resized ) && $resized ) {
   289 			unset( $saved['path'] );
   248 					unset( $resized['path'] );
   290 		}
   249 					$metadata[ $size ] = $resized;
   291 
   250 				}
   292 		return $saved;
   251 			}
       
   252 
       
   253 			$this->size = $orig_size;
       
   254 		}
       
   255 
       
   256 		return $metadata;
       
   257 	}
   293 	}
   258 
   294 
   259 	/**
   295 	/**
   260 	 * Crops Image.
   296 	 * Crops Image.
   261 	 *
   297 	 *
   269 	 * @param int  $dst_h   Optional. The destination height.
   305 	 * @param int  $dst_h   Optional. The destination height.
   270 	 * @param bool $src_abs Optional. If the source crop points are absolute.
   306 	 * @param bool $src_abs Optional. If the source crop points are absolute.
   271 	 * @return bool|WP_Error
   307 	 * @return bool|WP_Error
   272 	 */
   308 	 */
   273 	public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) {
   309 	public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) {
   274 		// If destination width/height isn't specified, use same as
   310 		// If destination width/height isn't specified,
   275 		// width/height from source.
   311 		// use same as width/height from source.
   276 		if ( ! $dst_w ) {
   312 		if ( ! $dst_w ) {
   277 			$dst_w = $src_w;
   313 			$dst_w = $src_w;
   278 		}
   314 		}
   279 		if ( ! $dst_h ) {
   315 		if ( ! $dst_h ) {
   280 			$dst_h = $src_h;
   316 			$dst_h = $src_h;
   377 
   413 
   378 		return $saved;
   414 		return $saved;
   379 	}
   415 	}
   380 
   416 
   381 	/**
   417 	/**
   382 	 * @param resource $image
   418 	 * @param resource    $image
   383 	 * @param string|null $filename
   419 	 * @param string|null $filename
   384 	 * @param string|null $mime_type
   420 	 * @param string|null $mime_type
   385 	 * @return WP_Error|array
   421 	 * @return array|WP_Error
   386 	 */
   422 	 */
   387 	protected function _save( $image, $filename = null, $mime_type = null ) {
   423 	protected function _save( $image, $filename = null, $mime_type = null ) {
   388 		list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
   424 		list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
   389 
   425 
   390 		if ( ! $filename ) {
   426 		if ( ! $filename ) {
   391 			$filename = $this->generate_filename( null, null, $extension );
   427 			$filename = $this->generate_filename( null, null, $extension );
   392 		}
   428 		}
   393 
   429 
   394 		if ( 'image/gif' == $mime_type ) {
   430 		if ( 'image/gif' === $mime_type ) {
   395 			if ( ! $this->make_image( $filename, 'imagegif', array( $image, $filename ) ) ) {
   431 			if ( ! $this->make_image( $filename, 'imagegif', array( $image, $filename ) ) ) {
   396 				return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
   432 				return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
   397 			}
   433 			}
   398 		} elseif ( 'image/png' == $mime_type ) {
   434 		} elseif ( 'image/png' === $mime_type ) {
   399 			// convert from full colors to index colors, like original PNG.
   435 			// Convert from full colors to index colors, like original PNG.
   400 			if ( function_exists( 'imageistruecolor' ) && ! imageistruecolor( $image ) ) {
   436 			if ( function_exists( 'imageistruecolor' ) && ! imageistruecolor( $image ) ) {
   401 				imagetruecolortopalette( $image, false, imagecolorstotal( $image ) );
   437 				imagetruecolortopalette( $image, false, imagecolorstotal( $image ) );
   402 			}
   438 			}
   403 
   439 
   404 			if ( ! $this->make_image( $filename, 'imagepng', array( $image, $filename ) ) ) {
   440 			if ( ! $this->make_image( $filename, 'imagepng', array( $image, $filename ) ) ) {
   405 				return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
   441 				return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
   406 			}
   442 			}
   407 		} elseif ( 'image/jpeg' == $mime_type ) {
   443 		} elseif ( 'image/jpeg' === $mime_type ) {
   408 			if ( ! $this->make_image( $filename, 'imagejpeg', array( $image, $filename, $this->get_quality() ) ) ) {
   444 			if ( ! $this->make_image( $filename, 'imagejpeg', array( $image, $filename, $this->get_quality() ) ) ) {
   409 				return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
   445 				return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
   410 			}
   446 			}
   411 		} else {
   447 		} else {
   412 			return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
   448 			return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) );
   413 		}
   449 		}
   414 
   450 
   415 		// Set correct file permissions
   451 		// Set correct file permissions.
   416 		$stat  = stat( dirname( $filename ) );
   452 		$stat  = stat( dirname( $filename ) );
   417 		$perms = $stat['mode'] & 0000666; //same permissions as parent folder, strip off the executable bits
   453 		$perms = $stat['mode'] & 0000666; // Same permissions as parent folder, strip off the executable bits.
   418 		@ chmod( $filename, $perms );
   454 		chmod( $filename, $perms );
   419 
   455 
   420 		/**
       
   421 		 * Filters the name of the saved image file.
       
   422 		 *
       
   423 		 * @since 2.6.0
       
   424 		 *
       
   425 		 * @param string $filename Name of the file.
       
   426 		 */
       
   427 		return array(
   456 		return array(
   428 			'path'      => $filename,
   457 			'path'      => $filename,
       
   458 			/**
       
   459 			 * Filters the name of the saved image file.
       
   460 			 *
       
   461 			 * @since 2.6.0
       
   462 			 *
       
   463 			 * @param string $filename Name of the file.
       
   464 			 */
   429 			'file'      => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ),
   465 			'file'      => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ),
   430 			'width'     => $this->size['width'],
   466 			'width'     => $this->size['width'],
   431 			'height'    => $this->size['height'],
   467 			'height'    => $this->size['height'],
   432 			'mime-type' => $mime_type,
   468 			'mime-type' => $mime_type,
   433 		);
   469 		);
   461 	 * Either calls editor's save function or handles file as a stream.
   497 	 * Either calls editor's save function or handles file as a stream.
   462 	 *
   498 	 *
   463 	 * @since 3.5.0
   499 	 * @since 3.5.0
   464 	 *
   500 	 *
   465 	 * @param string|stream $filename
   501 	 * @param string|stream $filename
   466 	 * @param callable $function
   502 	 * @param callable      $function
   467 	 * @param array $arguments
   503 	 * @param array         $arguments
   468 	 * @return bool
   504 	 * @return bool
   469 	 */
   505 	 */
   470 	protected function make_image( $filename, $function, $arguments ) {
   506 	protected function make_image( $filename, $function, $arguments ) {
   471 		if ( wp_is_stream( $filename ) ) {
   507 		if ( wp_is_stream( $filename ) ) {
   472 			$arguments[1] = null;
   508 			$arguments[1] = null;