WP_Image_Editor_GD

WordPress Image Editor Class for Image Manipulation through GD.

Defined (1)

The class is defined in the following location(s).

/wp-includes/class-wp-image-editor-gd.php  
  1. class WP_Image_Editor_GD extends WP_Image_Editor { 
  2. /** 
  3. * GD Resource. 
  4. * @access protected 
  5. * @var resource 
  6. */ 
  7. protected $image; 
  8.  
  9. public function __destruct() { 
  10. if ( $this->image ) { 
  11. // we don't need the original in memory anymore 
  12. imagedestroy( $this->image ); 
  13.  
  14. /** 
  15. * Checks to see if current environment supports GD. 
  16. * @since 3.5.0 
  17. * @static 
  18. * @access public 
  19. * @param array $args 
  20. * @return bool 
  21. */ 
  22. public static function test( $args = array() ) { 
  23. if ( ! extension_loaded('gd') || ! function_exists('gd_info') ) 
  24. return false; 
  25.  
  26. // On some setups GD library does not provide imagerotate() - Ticket #11536 
  27. if ( isset( $args['methods'] ) && 
  28. in_array( 'rotate', $args['methods'] ) && 
  29. ! function_exists('imagerotate') ) { 
  30.  
  31. return false; 
  32.  
  33. return true; 
  34.  
  35. /** 
  36. * Checks to see if editor supports the mime-type specified. 
  37. * @since 3.5.0 
  38. * @static 
  39. * @access public 
  40. * @param string $mime_type 
  41. * @return bool 
  42. */ 
  43. public static function supports_mime_type( $mime_type ) { 
  44. $image_types = imagetypes(); 
  45. switch( $mime_type ) { 
  46. case 'image/jpeg': 
  47. return ($image_types & IMG_JPG) != 0; 
  48. case 'image/png': 
  49. return ($image_types & IMG_PNG) != 0; 
  50. case 'image/gif': 
  51. return ($image_types & IMG_GIF) != 0; 
  52.  
  53. return false; 
  54.  
  55. /** 
  56. * Loads image from $this->file into new GD Resource. 
  57. * @since 3.5.0 
  58. * @access protected 
  59. * @return bool|WP_Error True if loaded successfully; WP_Error on failure. 
  60. */ 
  61. public function load() { 
  62. if ( $this->image ) 
  63. return true; 
  64.  
  65. if ( ! is_file( $this->file ) && ! preg_match( '|^https?://|', $this->file ) ) 
  66. return new WP_Error( 'error_loading_image', __('File doesn’t exist?'), $this->file ); 
  67.  
  68. // Set artificially high because GD uses uncompressed images in memory. 
  69. wp_raise_memory_limit( 'image' ); 
  70.  
  71. $this->image = @imagecreatefromstring( file_get_contents( $this->file ) ); 
  72.  
  73. if ( ! is_resource( $this->image ) ) 
  74. return new WP_Error( 'invalid_image', __('File is not an image.'), $this->file ); 
  75.  
  76. $size = @getimagesize( $this->file ); 
  77. if ( ! $size ) 
  78. return new WP_Error( 'invalid_image', __('Could not read image size.'), $this->file ); 
  79.  
  80. if ( function_exists( 'imagealphablending' ) && function_exists( 'imagesavealpha' ) ) { 
  81. imagealphablending( $this->image, false ); 
  82. imagesavealpha( $this->image, true ); 
  83.  
  84. $this->update_size( $size[0], $size[1] ); 
  85. $this->mime_type = $size['mime']; 
  86.  
  87. return $this->set_quality(); 
  88.  
  89. /** 
  90. * Sets or updates current image size. 
  91. * @since 3.5.0 
  92. * @access protected 
  93. * @param int $width 
  94. * @param int $height 
  95. * @return true 
  96. */ 
  97. protected function update_size( $width = false, $height = false ) { 
  98. if ( ! $width ) 
  99. $width = imagesx( $this->image ); 
  100.  
  101. if ( ! $height ) 
  102. $height = imagesy( $this->image ); 
  103.  
  104. return parent::update_size( $width, $height ); 
  105.  
  106. /** 
  107. * Resizes current image. 
  108. * Wraps _resize, since _resize returns a GD Resource. 
  109. * At minimum, either a height or width must be provided. 
  110. * If one of the two is set to null, the resize will 
  111. * maintain aspect ratio according to the provided dimension. 
  112. * @since 3.5.0 
  113. * @access public 
  114. * @param int|null $max_w Image width. 
  115. * @param int|null $max_h Image height. 
  116. * @param bool $crop 
  117. * @return true|WP_Error 
  118. */ 
  119. public function resize( $max_w, $max_h, $crop = false ) { 
  120. if ( ( $this->size['width'] == $max_w ) && ( $this->size['height'] == $max_h ) ) 
  121. return true; 
  122.  
  123. $resized = $this->_resize( $max_w, $max_h, $crop ); 
  124.  
  125. if ( is_resource( $resized ) ) { 
  126. imagedestroy( $this->image ); 
  127. $this->image = $resized; 
  128. return true; 
  129.  
  130. } elseif ( is_wp_error( $resized ) ) 
  131. return $resized; 
  132.  
  133. return new WP_Error( 'image_resize_error', __('Image resize failed.'), $this->file ); 
  134.  
  135. /** 
  136. * @param int $max_w 
  137. * @param int $max_h 
  138. * @param bool|array $crop 
  139. * @return resource|WP_Error 
  140. */ 
  141. protected function _resize( $max_w, $max_h, $crop = false ) { 
  142. $dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop ); 
  143. if ( ! $dims ) { 
  144. return new WP_Error( 'error_getting_dimensions', __('Could not calculate resized image dimensions'), $this->file ); 
  145. list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims; 
  146.  
  147. $resized = wp_imagecreatetruecolor( $dst_w, $dst_h ); 
  148. imagecopyresampled( $resized, $this->image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ); 
  149.  
  150. if ( is_resource( $resized ) ) { 
  151. $this->update_size( $dst_w, $dst_h ); 
  152. return $resized; 
  153.  
  154. return new WP_Error( 'image_resize_error', __('Image resize failed.'), $this->file ); 
  155.  
  156. /** 
  157. * Resize multiple images from a single source. 
  158. * @since 3.5.0 
  159. * @access public 
  160. * @param array $sizes { 
  161. * An array of image size arrays. Default sizes are 'small', 'medium', 'medium_large', 'large'. 
  162. * Either a height or width must be provided. 
  163. * If one of the two is set to null, the resize will 
  164. * maintain aspect ratio according to the provided dimension. 
  165. * @type array $size { 
  166. * Array of height, width values, and whether to crop. 
  167. * @type int $width Image width. Optional if `$height` is specified. 
  168. * @type int $height Image height. Optional if `$width` is specified. 
  169. * @type bool $crop Optional. Whether to crop the image. Default false. 
  170. * } 
  171. * } 
  172. * @return array An array of resized images' metadata by size. 
  173. */ 
  174. public function multi_resize( $sizes ) { 
  175. $metadata = array(); 
  176. $orig_size = $this->size; 
  177.  
  178. foreach ( $sizes as $size => $size_data ) { 
  179. if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) { 
  180. continue; 
  181.  
  182. if ( ! isset( $size_data['width'] ) ) { 
  183. $size_data['width'] = null; 
  184. if ( ! isset( $size_data['height'] ) ) { 
  185. $size_data['height'] = null; 
  186.  
  187. if ( ! isset( $size_data['crop'] ) ) { 
  188. $size_data['crop'] = false; 
  189.  
  190. $image = $this->_resize( $size_data['width'], $size_data['height'], $size_data['crop'] ); 
  191. $duplicate = ( ( $orig_size['width'] == $size_data['width'] ) && ( $orig_size['height'] == $size_data['height'] ) ); 
  192.  
  193. if ( ! is_wp_error( $image ) && ! $duplicate ) { 
  194. $resized = $this->_save( $image ); 
  195.  
  196. imagedestroy( $image ); 
  197.  
  198. if ( ! is_wp_error( $resized ) && $resized ) { 
  199. unset( $resized['path'] ); 
  200. $metadata[$size] = $resized; 
  201.  
  202. $this->size = $orig_size; 
  203.  
  204. return $metadata; 
  205.  
  206. /** 
  207. * Crops Image. 
  208. * @since 3.5.0 
  209. * @access public 
  210. * @param int $src_x The start x position to crop from. 
  211. * @param int $src_y The start y position to crop from. 
  212. * @param int $src_w The width to crop. 
  213. * @param int $src_h The height to crop. 
  214. * @param int $dst_w Optional. The destination width. 
  215. * @param int $dst_h Optional. The destination height. 
  216. * @param bool $src_abs Optional. If the source crop points are absolute. 
  217. * @return bool|WP_Error 
  218. */ 
  219. public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) { 
  220. // If destination width/height isn't specified, use same as 
  221. // width/height from source. 
  222. if ( ! $dst_w ) 
  223. $dst_w = $src_w; 
  224. if ( ! $dst_h ) 
  225. $dst_h = $src_h; 
  226.  
  227. $dst = wp_imagecreatetruecolor( $dst_w, $dst_h ); 
  228.  
  229. if ( $src_abs ) { 
  230. $src_w -= $src_x; 
  231. $src_h -= $src_y; 
  232.  
  233. if ( function_exists( 'imageantialias' ) ) 
  234. imageantialias( $dst, true ); 
  235.  
  236. imagecopyresampled( $dst, $this->image, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ); 
  237.  
  238. if ( is_resource( $dst ) ) { 
  239. imagedestroy( $this->image ); 
  240. $this->image = $dst; 
  241. $this->update_size(); 
  242. return true; 
  243.  
  244. return new WP_Error( 'image_crop_error', __('Image crop failed.'), $this->file ); 
  245.  
  246. /** 
  247. * Rotates current image counter-clockwise by $angle. 
  248. * Ported from image-edit.php 
  249. * @since 3.5.0 
  250. * @access public 
  251. * @param float $angle 
  252. * @return true|WP_Error 
  253. */ 
  254. public function rotate( $angle ) { 
  255. if ( function_exists('imagerotate') ) { 
  256. $transparency = imagecolorallocatealpha( $this->image, 255, 255, 255, 127 ); 
  257. $rotated = imagerotate( $this->image, $angle, $transparency ); 
  258.  
  259. if ( is_resource( $rotated ) ) { 
  260. imagealphablending( $rotated, true ); 
  261. imagesavealpha( $rotated, true ); 
  262. imagedestroy( $this->image ); 
  263. $this->image = $rotated; 
  264. $this->update_size(); 
  265. return true; 
  266. return new WP_Error( 'image_rotate_error', __('Image rotate failed.'), $this->file ); 
  267.  
  268. /** 
  269. * Flips current image. 
  270. * @since 3.5.0 
  271. * @access public 
  272. * @param bool $horz Flip along Horizontal Axis 
  273. * @param bool $vert Flip along Vertical Axis 
  274. * @return true|WP_Error 
  275. */ 
  276. public function flip( $horz, $vert ) { 
  277. $w = $this->size['width']; 
  278. $h = $this->size['height']; 
  279. $dst = wp_imagecreatetruecolor( $w, $h ); 
  280.  
  281. if ( is_resource( $dst ) ) { 
  282. $sx = $vert ? ($w - 1) : 0; 
  283. $sy = $horz ? ($h - 1) : 0; 
  284. $sw = $vert ? -$w : $w; 
  285. $sh = $horz ? -$h : $h; 
  286.  
  287. if ( imagecopyresampled( $dst, $this->image, 0, 0, $sx, $sy, $w, $h, $sw, $sh ) ) { 
  288. imagedestroy( $this->image ); 
  289. $this->image = $dst; 
  290. return true; 
  291. return new WP_Error( 'image_flip_error', __('Image flip failed.'), $this->file ); 
  292.  
  293. /** 
  294. * Saves current in-memory image to file. 
  295. * @since 3.5.0 
  296. * @access public 
  297. * @param string|null $filename 
  298. * @param string|null $mime_type 
  299. * @return array|WP_Error {'path'=>string, 'file'=>string, 'width'=>int, 'height'=>int, 'mime-type'=>string} 
  300. */ 
  301. public function save( $filename = null, $mime_type = null ) { 
  302. $saved = $this->_save( $this->image, $filename, $mime_type ); 
  303.  
  304. if ( ! is_wp_error( $saved ) ) { 
  305. $this->file = $saved['path']; 
  306. $this->mime_type = $saved['mime-type']; 
  307.  
  308. return $saved; 
  309.  
  310. /** 
  311. * @param resource $image 
  312. * @param string|null $filename 
  313. * @param string|null $mime_type 
  314. * @return WP_Error|array 
  315. */ 
  316. protected function _save( $image, $filename = null, $mime_type = null ) { 
  317. list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type ); 
  318.  
  319. if ( ! $filename ) 
  320. $filename = $this->generate_filename( null, null, $extension ); 
  321.  
  322. if ( 'image/gif' == $mime_type ) { 
  323. if ( ! $this->make_image( $filename, 'imagegif', array( $image, $filename ) ) ) 
  324. return new WP_Error( 'image_save_error', __('Image Editor Save Failed') ); 
  325. elseif ( 'image/png' == $mime_type ) { 
  326. // convert from full colors to index colors, like original PNG. 
  327. if ( function_exists('imageistruecolor') && ! imageistruecolor( $image ) ) 
  328. imagetruecolortopalette( $image, false, imagecolorstotal( $image ) ); 
  329.  
  330. if ( ! $this->make_image( $filename, 'imagepng', array( $image, $filename ) ) ) 
  331. return new WP_Error( 'image_save_error', __('Image Editor Save Failed') ); 
  332. elseif ( 'image/jpeg' == $mime_type ) { 
  333. if ( ! $this->make_image( $filename, 'imagejpeg', array( $image, $filename, $this->get_quality() ) ) ) 
  334. return new WP_Error( 'image_save_error', __('Image Editor Save Failed') ); 
  335. else { 
  336. return new WP_Error( 'image_save_error', __('Image Editor Save Failed') ); 
  337.  
  338. // Set correct file permissions 
  339. $stat = stat( dirname( $filename ) ); 
  340. $perms = $stat['mode'] & 0000666; //same permissions as parent folder, strip off the executable bits 
  341. @ chmod( $filename, $perms ); 
  342.  
  343. /** 
  344. * Filters the name of the saved image file. 
  345. * @since 2.6.0 
  346. * @param string $filename Name of the file. 
  347. */ 
  348. return array( 
  349. 'path' => $filename,  
  350. 'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ),  
  351. 'width' => $this->size['width'],  
  352. 'height' => $this->size['height'],  
  353. 'mime-type' => $mime_type,  
  354. ); 
  355.  
  356. /** 
  357. * Returns stream of current image. 
  358. * @since 3.5.0 
  359. * @access public 
  360. * @param string $mime_type 
  361. * @return bool 
  362. */ 
  363. public function stream( $mime_type = null ) { 
  364. list( $filename, $extension, $mime_type ) = $this->get_output_format( null, $mime_type ); 
  365.  
  366. switch ( $mime_type ) { 
  367. case 'image/png': 
  368. header( 'Content-Type: image/png' ); 
  369. return imagepng( $this->image ); 
  370. case 'image/gif': 
  371. header( 'Content-Type: image/gif' ); 
  372. return imagegif( $this->image ); 
  373. default: 
  374. header( 'Content-Type: image/jpeg' ); 
  375. return imagejpeg( $this->image, null, $this->get_quality() ); 
  376.  
  377. /** 
  378. * Either calls editor's save function or handles file as a stream. 
  379. * @since 3.5.0 
  380. * @access protected 
  381. * @param string|stream $filename 
  382. * @param callable $function 
  383. * @param array $arguments 
  384. * @return bool 
  385. */ 
  386. protected function make_image( $filename, $function, $arguments ) { 
  387. if ( wp_is_stream( $filename ) ) 
  388. $arguments[1] = null; 
  389.  
  390. return parent::make_image( $filename, $function, $arguments );