[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * WordPress GD Image Editor 4 * 5 * @package WordPress 6 * @subpackage Image_Editor 7 */ 8 9 /** 10 * WordPress Image Editor Class for Image Manipulation through GD 11 * 12 * @since 3.5.0 13 * 14 * @see WP_Image_Editor 15 */ 16 class WP_Image_Editor_GD extends WP_Image_Editor { 17 /** 18 * GD Resource. 19 * 20 * @var resource|GdImage 21 */ 22 protected $image; 23 24 public function __destruct() { 25 if ( $this->image ) { 26 // We don't need the original in memory anymore. 27 imagedestroy( $this->image ); 28 } 29 } 30 31 /** 32 * Checks to see if current environment supports GD. 33 * 34 * @since 3.5.0 35 * 36 * @param array $args 37 * @return bool 38 */ 39 public static function test( $args = array() ) { 40 if ( ! extension_loaded( 'gd' ) || ! function_exists( 'gd_info' ) ) { 41 return false; 42 } 43 44 // On some setups GD library does not provide imagerotate() - Ticket #11536. 45 if ( isset( $args['methods'] ) && 46 in_array( 'rotate', $args['methods'], true ) && 47 ! function_exists( 'imagerotate' ) ) { 48 49 return false; 50 } 51 52 return true; 53 } 54 55 /** 56 * Checks to see if editor supports the mime-type specified. 57 * 58 * @since 3.5.0 59 * 60 * @param string $mime_type 61 * @return bool 62 */ 63 public static function supports_mime_type( $mime_type ) { 64 $image_types = imagetypes(); 65 switch ( $mime_type ) { 66 case 'image/jpeg': 67 return ( $image_types & IMG_JPG ) != 0; 68 case 'image/png': 69 return ( $image_types & IMG_PNG ) != 0; 70 case 'image/gif': 71 return ( $image_types & IMG_GIF ) != 0; 72 } 73 74 return false; 75 } 76 77 /** 78 * Loads image from $this->file into new GD Resource. 79 * 80 * @since 3.5.0 81 * 82 * @return true|WP_Error True if loaded successfully; WP_Error on failure. 83 */ 84 public function load() { 85 if ( $this->image ) { 86 return true; 87 } 88 89 if ( ! is_file( $this->file ) && ! preg_match( '|^https?://|', $this->file ) ) { 90 return new WP_Error( 'error_loading_image', __( 'File doesn’t exist?' ), $this->file ); 91 } 92 93 // Set artificially high because GD uses uncompressed images in memory. 94 wp_raise_memory_limit( 'image' ); 95 96 $file_contents = @file_get_contents( $this->file ); 97 98 if ( ! $file_contents ) { 99 return new WP_Error( 'error_loading_image', __( 'File doesn’t exist?' ), $this->file ); 100 } 101 102 $this->image = @imagecreatefromstring( $file_contents ); 103 104 if ( ! is_gd_image( $this->image ) ) { 105 return new WP_Error( 'invalid_image', __( 'File is not an image.' ), $this->file ); 106 } 107 108 $size = @getimagesize( $this->file ); 109 110 if ( ! $size ) { 111 return new WP_Error( 'invalid_image', __( 'Could not read image size.' ), $this->file ); 112 } 113 114 if ( function_exists( 'imagealphablending' ) && function_exists( 'imagesavealpha' ) ) { 115 imagealphablending( $this->image, false ); 116 imagesavealpha( $this->image, true ); 117 } 118 119 $this->update_size( $size[0], $size[1] ); 120 $this->mime_type = $size['mime']; 121 122 return $this->set_quality(); 123 } 124 125 /** 126 * Sets or updates current image size. 127 * 128 * @since 3.5.0 129 * 130 * @param int $width 131 * @param int $height 132 * @return true 133 */ 134 protected function update_size( $width = false, $height = false ) { 135 if ( ! $width ) { 136 $width = imagesx( $this->image ); 137 } 138 139 if ( ! $height ) { 140 $height = imagesy( $this->image ); 141 } 142 143 return parent::update_size( $width, $height ); 144 } 145 146 /** 147 * Resizes current image. 148 * 149 * Wraps `::_resize()` which returns a GD resource or GdImage instance. 150 * 151 * At minimum, either a height or width must be provided. If one of the two is set 152 * to null, the resize will maintain aspect ratio according to the provided dimension. 153 * 154 * @since 3.5.0 155 * 156 * @param int|null $max_w Image width. 157 * @param int|null $max_h Image height. 158 * @param bool $crop 159 * @return true|WP_Error 160 */ 161 public function resize( $max_w, $max_h, $crop = false ) { 162 if ( ( $this->size['width'] == $max_w ) && ( $this->size['height'] == $max_h ) ) { 163 return true; 164 } 165 166 $resized = $this->_resize( $max_w, $max_h, $crop ); 167 168 if ( is_gd_image( $resized ) ) { 169 imagedestroy( $this->image ); 170 $this->image = $resized; 171 return true; 172 173 } elseif ( is_wp_error( $resized ) ) { 174 return $resized; 175 } 176 177 return new WP_Error( 'image_resize_error', __( 'Image resize failed.' ), $this->file ); 178 } 179 180 /** 181 * @param int $max_w 182 * @param int $max_h 183 * @param bool|array $crop 184 * @return resource|GdImage|WP_Error 185 */ 186 protected function _resize( $max_w, $max_h, $crop = false ) { 187 $dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop ); 188 189 if ( ! $dims ) { 190 return new WP_Error( 'error_getting_dimensions', __( 'Could not calculate resized image dimensions' ), $this->file ); 191 } 192 193 list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims; 194 195 $resized = wp_imagecreatetruecolor( $dst_w, $dst_h ); 196 imagecopyresampled( $resized, $this->image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ); 197 198 if ( is_gd_image( $resized ) ) { 199 $this->update_size( $dst_w, $dst_h ); 200 return $resized; 201 } 202 203 return new WP_Error( 'image_resize_error', __( 'Image resize failed.' ), $this->file ); 204 } 205 206 /** 207 * Create multiple smaller images from a single source. 208 * 209 * Attempts to create all sub-sizes and returns the meta data at the end. This 210 * may result in the server running out of resources. When it fails there may be few 211 * "orphaned" images left over as the meta data is never returned and saved. 212 * 213 * As of 5.3.0 the preferred way to do this is with `make_subsize()`. It creates 214 * the new images one at a time and allows for the meta data to be saved after 215 * each new image is created. 216 * 217 * @since 3.5.0 218 * 219 * @param array $sizes { 220 * An array of image size data arrays. 221 * 222 * Either a height or width must be provided. 223 * If one of the two is set to null, the resize will 224 * maintain aspect ratio according to the source image. 225 * 226 * @type array $size { 227 * Array of height, width values, and whether to crop. 228 * 229 * @type int $width Image width. Optional if `$height` is specified. 230 * @type int $height Image height. Optional if `$width` is specified. 231 * @type bool $crop Optional. Whether to crop the image. Default false. 232 * } 233 * } 234 * @return array An array of resized images' metadata by size. 235 */ 236 public function multi_resize( $sizes ) { 237 $metadata = array(); 238 239 foreach ( $sizes as $size => $size_data ) { 240 $meta = $this->make_subsize( $size_data ); 241 242 if ( ! is_wp_error( $meta ) ) { 243 $metadata[ $size ] = $meta; 244 } 245 } 246 247 return $metadata; 248 } 249 250 /** 251 * Create an image sub-size and return the image meta data value for it. 252 * 253 * @since 5.3.0 254 * 255 * @param array $size_data { 256 * Array of size data. 257 * 258 * @type int $width The maximum width in pixels. 259 * @type int $height The maximum height in pixels. 260 * @type bool $crop Whether to crop the image to exact dimensions. 261 * } 262 * @return array|WP_Error The image data array for inclusion in the `sizes` array in the image meta, 263 * WP_Error object on error. 264 */ 265 public function make_subsize( $size_data ) { 266 if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) { 267 return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) ); 268 } 269 270 $orig_size = $this->size; 271 272 if ( ! isset( $size_data['width'] ) ) { 273 $size_data['width'] = null; 274 } 275 276 if ( ! isset( $size_data['height'] ) ) { 277 $size_data['height'] = null; 278 } 279 280 if ( ! isset( $size_data['crop'] ) ) { 281 $size_data['crop'] = false; 282 } 283 284 $resized = $this->_resize( $size_data['width'], $size_data['height'], $size_data['crop'] ); 285 286 if ( is_wp_error( $resized ) ) { 287 $saved = $resized; 288 } else { 289 $saved = $this->_save( $resized ); 290 imagedestroy( $resized ); 291 } 292 293 $this->size = $orig_size; 294 295 if ( ! is_wp_error( $saved ) ) { 296 unset( $saved['path'] ); 297 } 298 299 return $saved; 300 } 301 302 /** 303 * Crops Image. 304 * 305 * @since 3.5.0 306 * 307 * @param int $src_x The start x position to crop from. 308 * @param int $src_y The start y position to crop from. 309 * @param int $src_w The width to crop. 310 * @param int $src_h The height to crop. 311 * @param int $dst_w Optional. The destination width. 312 * @param int $dst_h Optional. The destination height. 313 * @param bool $src_abs Optional. If the source crop points are absolute. 314 * @return true|WP_Error 315 */ 316 public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) { 317 // If destination width/height isn't specified, 318 // use same as width/height from source. 319 if ( ! $dst_w ) { 320 $dst_w = $src_w; 321 } 322 if ( ! $dst_h ) { 323 $dst_h = $src_h; 324 } 325 326 foreach ( array( $src_w, $src_h, $dst_w, $dst_h ) as $value ) { 327 if ( ! is_numeric( $value ) || (int) $value <= 0 ) { 328 return new WP_Error( 'image_crop_error', __( 'Image crop failed.' ), $this->file ); 329 } 330 } 331 332 $dst = wp_imagecreatetruecolor( (int) $dst_w, (int) $dst_h ); 333 334 if ( $src_abs ) { 335 $src_w -= $src_x; 336 $src_h -= $src_y; 337 } 338 339 if ( function_exists( 'imageantialias' ) ) { 340 imageantialias( $dst, true ); 341 } 342 343 imagecopyresampled( $dst, $this->image, 0, 0, (int) $src_x, (int) $src_y, (int) $dst_w, (int) $dst_h, (int) $src_w, (int) $src_h ); 344 345 if ( is_gd_image( $dst ) ) { 346 imagedestroy( $this->image ); 347 $this->image = $dst; 348 $this->update_size(); 349 return true; 350 } 351 352 return new WP_Error( 'image_crop_error', __( 'Image crop failed.' ), $this->file ); 353 } 354 355 /** 356 * Rotates current image counter-clockwise by $angle. 357 * Ported from image-edit.php 358 * 359 * @since 3.5.0 360 * 361 * @param float $angle 362 * @return true|WP_Error 363 */ 364 public function rotate( $angle ) { 365 if ( function_exists( 'imagerotate' ) ) { 366 $transparency = imagecolorallocatealpha( $this->image, 255, 255, 255, 127 ); 367 $rotated = imagerotate( $this->image, $angle, $transparency ); 368 369 if ( is_gd_image( $rotated ) ) { 370 imagealphablending( $rotated, true ); 371 imagesavealpha( $rotated, true ); 372 imagedestroy( $this->image ); 373 $this->image = $rotated; 374 $this->update_size(); 375 return true; 376 } 377 } 378 379 return new WP_Error( 'image_rotate_error', __( 'Image rotate failed.' ), $this->file ); 380 } 381 382 /** 383 * Flips current image. 384 * 385 * @since 3.5.0 386 * 387 * @param bool $horz Flip along Horizontal Axis. 388 * @param bool $vert Flip along Vertical Axis. 389 * @return true|WP_Error 390 */ 391 public function flip( $horz, $vert ) { 392 $w = $this->size['width']; 393 $h = $this->size['height']; 394 $dst = wp_imagecreatetruecolor( $w, $h ); 395 396 if ( is_gd_image( $dst ) ) { 397 $sx = $vert ? ( $w - 1 ) : 0; 398 $sy = $horz ? ( $h - 1 ) : 0; 399 $sw = $vert ? -$w : $w; 400 $sh = $horz ? -$h : $h; 401 402 if ( imagecopyresampled( $dst, $this->image, 0, 0, $sx, $sy, $w, $h, $sw, $sh ) ) { 403 imagedestroy( $this->image ); 404 $this->image = $dst; 405 return true; 406 } 407 } 408 409 return new WP_Error( 'image_flip_error', __( 'Image flip failed.' ), $this->file ); 410 } 411 412 /** 413 * Saves current in-memory image to file. 414 * 415 * @since 3.5.0 416 * 417 * @param string|null $filename 418 * @param string|null $mime_type 419 * @return array|WP_Error {'path'=>string, 'file'=>string, 'width'=>int, 'height'=>int, 'mime-type'=>string} 420 */ 421 public function save( $filename = null, $mime_type = null ) { 422 $saved = $this->_save( $this->image, $filename, $mime_type ); 423 424 if ( ! is_wp_error( $saved ) ) { 425 $this->file = $saved['path']; 426 $this->mime_type = $saved['mime-type']; 427 } 428 429 return $saved; 430 } 431 432 /** 433 * @param resource|GdImage $image 434 * @param string|null $filename 435 * @param string|null $mime_type 436 * @return array|WP_Error 437 */ 438 protected function _save( $image, $filename = null, $mime_type = null ) { 439 list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type ); 440 441 if ( ! $filename ) { 442 $filename = $this->generate_filename( null, null, $extension ); 443 } 444 445 if ( 'image/gif' === $mime_type ) { 446 if ( ! $this->make_image( $filename, 'imagegif', array( $image, $filename ) ) ) { 447 return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) ); 448 } 449 } elseif ( 'image/png' === $mime_type ) { 450 // Convert from full colors to index colors, like original PNG. 451 if ( function_exists( 'imageistruecolor' ) && ! imageistruecolor( $image ) ) { 452 imagetruecolortopalette( $image, false, imagecolorstotal( $image ) ); 453 } 454 455 if ( ! $this->make_image( $filename, 'imagepng', array( $image, $filename ) ) ) { 456 return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) ); 457 } 458 } elseif ( 'image/jpeg' === $mime_type ) { 459 if ( ! $this->make_image( $filename, 'imagejpeg', array( $image, $filename, $this->get_quality() ) ) ) { 460 return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) ); 461 } 462 } else { 463 return new WP_Error( 'image_save_error', __( 'Image Editor Save Failed' ) ); 464 } 465 466 // Set correct file permissions. 467 $stat = stat( dirname( $filename ) ); 468 $perms = $stat['mode'] & 0000666; // Same permissions as parent folder, strip off the executable bits. 469 chmod( $filename, $perms ); 470 471 return array( 472 'path' => $filename, 473 /** 474 * Filters the name of the saved image file. 475 * 476 * @since 2.6.0 477 * 478 * @param string $filename Name of the file. 479 */ 480 'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ), 481 'width' => $this->size['width'], 482 'height' => $this->size['height'], 483 'mime-type' => $mime_type, 484 ); 485 } 486 487 /** 488 * Returns stream of current image. 489 * 490 * @since 3.5.0 491 * 492 * @param string $mime_type The mime type of the image. 493 * @return bool True on success, false on failure. 494 */ 495 public function stream( $mime_type = null ) { 496 list( $filename, $extension, $mime_type ) = $this->get_output_format( null, $mime_type ); 497 498 switch ( $mime_type ) { 499 case 'image/png': 500 header( 'Content-Type: image/png' ); 501 return imagepng( $this->image ); 502 case 'image/gif': 503 header( 'Content-Type: image/gif' ); 504 return imagegif( $this->image ); 505 default: 506 header( 'Content-Type: image/jpeg' ); 507 return imagejpeg( $this->image, null, $this->get_quality() ); 508 } 509 } 510 511 /** 512 * Either calls editor's save function or handles file as a stream. 513 * 514 * @since 3.5.0 515 * 516 * @param string|stream $filename 517 * @param callable $function 518 * @param array $arguments 519 * @return bool 520 */ 521 protected function make_image( $filename, $function, $arguments ) { 522 if ( wp_is_stream( $filename ) ) { 523 $arguments[1] = null; 524 } 525 526 return parent::make_image( $filename, $function, $arguments ); 527 } 528 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Mon Jan 18 08:20:02 2021 | Cross-referenced by PHPXref |