Skip to content

Commit f96f9db

Browse files
committed
First version of the WP_Image_Editor
1 parent d7f7240 commit f96f9db

File tree

8 files changed

+330
-125
lines changed

8 files changed

+330
-125
lines changed

wp-admin/includes/image.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,16 @@ function wp_generate_attachment_metadata( $attachment_id, $file ) {
144144

145145
$sizes = apply_filters( 'intermediate_image_sizes_advanced', $sizes );
146146

147+
$editor = new WP_Image_Editor( $file );
148+
147149
foreach ($sizes as $size => $size_data ) {
148-
$resized = image_make_intermediate_size( $file, $size_data['width'], $size_data['height'], $size_data['crop'] );
149-
if ( $resized )
150+
$resized = $editor->resize( $size_data['width'], $size_data['height'], $size_data['crop'] );
151+
if ( ! is_wp_error( $resized ) && $resized )
150152
$metadata['sizes'][$size] = $resized;
151153
}
152154

155+
unset( $editor );
156+
153157
// fetch additional metadata from exif/iptc
154158
$image_meta = wp_read_image_metadata( $file );
155159
if ( $image_meta )

wp-includes/class-wp-image-editor.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
class WP_Image_Editor {
4+
5+
final public static function get_instance( $path ) {
6+
$implementation = apply_filters( 'image_editor_class', self::choose_implementation(), $path );
7+
8+
if ( $implementation )
9+
return new $implementation( $path );
10+
11+
return false;
12+
}
13+
14+
/**
15+
* Tests which editors are capable of supporting the request.
16+
*
17+
* @since 3.5.0
18+
* @access private
19+
*
20+
* @return string|bool Class name for the first editor that claims to support the request. False if no editor claims to support the request.
21+
*/
22+
private static function choose_implementation() {
23+
static $implementation;
24+
25+
if ( null === $implementation ) {
26+
$request_order = apply_filters( 'wp_editors', array( 'imagick', 'gd' ) );
27+
28+
// Loop over each editor on each request looking for one which will serve this request's needs
29+
foreach ( $request_order as $editor ) {
30+
$class = 'WP_Image_Editor_' . $editor;
31+
32+
// Check to see if this editor is a possibility, calls the editor statically
33+
if ( ! call_user_func( array( $class, 'test' ) ) )
34+
continue;
35+
36+
if( ! apply_filters( 'wp_editor_use_' . $editor, true ) )
37+
continue;
38+
39+
$implementation = $class;
40+
}
41+
}
42+
43+
return $implementation;
44+
}
45+
}

wp-includes/deprecated.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3203,4 +3203,55 @@ function sticky_class( $post_id = null ) {
32033203
*/
32043204
function _get_post_ancestors( &$post ) {
32053205
_deprecated_function( __FUNCTION__, '3.5' );
3206+
}
3207+
3208+
3209+
/**
3210+
* Load an image from a string, if PHP supports it.
3211+
*
3212+
* @since 2.1.0
3213+
* @deprecated 3.5.0
3214+
* @deprecated wp_get_image_for_editing()
3215+
*
3216+
* @param string $file Filename of the image to load.
3217+
* @return resource The resulting image resource on success, Error string on failure.
3218+
*/
3219+
function wp_load_image( $file ) {
3220+
_deprecated_function( __FUNCTION__, '3.5', 'wp_get_image_for_editing()' );
3221+
3222+
if ( is_numeric( $file ) )
3223+
$file = get_attached_file( $file );
3224+
3225+
$editor = new WP_Image_Editor_GD;
3226+
$editor->load( $file );
3227+
}
3228+
3229+
/**
3230+
* Scale down an image to fit a particular size and save a new copy of the image.
3231+
*
3232+
* The PNG transparency will be preserved using the function, as well as the
3233+
* image type. If the file going in is PNG, then the resized image is going to
3234+
* be PNG. The only supported image types are PNG, GIF, and JPEG.
3235+
*
3236+
* Some functionality requires API to exist, so some PHP version may lose out
3237+
* support. This is not the fault of WordPress (where functionality is
3238+
* downgraded, not actual defects), but of your PHP version.
3239+
*
3240+
* @since 2.5.0
3241+
* @deprecated 3.5.0
3242+
* @deprecated wp_get_image_for_editing()
3243+
*
3244+
* @param string $file Image file path.
3245+
* @param int $max_w Maximum width to resize to.
3246+
* @param int $max_h Maximum height to resize to.
3247+
* @param bool $crop Optional. Whether to crop image or resize.
3248+
* @param string $suffix Optional. File suffix.
3249+
* @param string $dest_path Optional. New image file path.
3250+
* @param int $jpeg_quality Optional, default is 90. Image quality percentage.
3251+
* @return mixed WP_Error on failure. String with new destination path.
3252+
*/
3253+
function image_resize( $file, $max_w, $max_h, $crop = false, $suffix = null, $dest_path = null, $jpeg_quality = 90 ) {
3254+
_deprecated_function( __FUNCTION__, '3.5', 'wp_get_image_for_editing()' );
3255+
3256+
$editor = new WP_Image_Editor_GD;+ $editor->resize( $file, $max_w, $max_h, $crop, $suffix, $dest_path, $jpeg_quality )
32063257
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
class WP_Image_Editor_Base {
4+
protected $file = false;
5+
protected $size = false;
6+
protected $orig_type = false;
7+
8+
function __construct( $filename ) {
9+
$this->file = $filename;
10+
}
11+
12+
public static function test() {
13+
return false;
14+
}
15+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?php
2+
3+
class WP_Image_Editor_GD extends WP_Image_Editor_Base {
4+
private $image = false;
5+
6+
function __destruct() {
7+
if ( $this->image ) {
8+
// we don't need the original in memory anymore
9+
imagedestroy( $this->image );
10+
}
11+
}
12+
13+
public static function test() {
14+
if ( ! extension_loaded('gd') || ! function_exists('gd_info') )
15+
return false;
16+
17+
return true;
18+
}
19+
20+
private function load() {
21+
if( $this->image )
22+
return true;
23+
24+
if ( ! file_exists( $this->file ) )
25+
return sprintf( __('File &#8220;%s&#8221; doesn&#8217;t exist?'), $this->file );
26+
27+
if ( ! function_exists('imagecreatefromstring') )
28+
return __('The GD image library is not installed.');
29+
30+
// Set artificially high because GD uses uncompressed images in memory
31+
@ini_set( 'memory_limit', apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT ) );
32+
$this->image = imagecreatefromstring( file_get_contents( $this->file ) );
33+
34+
if ( ! is_resource( $this->image ) )
35+
return sprintf( __('File &#8220;%s&#8221; is not an image.'), $this->file );
36+
37+
$size = @getimagesize( $this->file );
38+
if ( ! $size )
39+
return new WP_Error( 'invalid_image', __('Could not read image size'), $this->file );
40+
41+
$this->size = array( 'width' => $size[0], 'height' => $size[1] );
42+
$this->orig_type = $size[1];
43+
44+
return true;
45+
}
46+
47+
public function resize( $max_w, $max_h, $crop = false, $suffix = null, $dest_path = null, $jpeg_quality = 90 ) {
48+
if ( ! $this->load() )
49+
return;
50+
51+
$dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop );
52+
if ( ! $dims )
53+
return new WP_Error( 'error_getting_dimensions', __('Could not calculate resized image dimensions') );
54+
list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims;
55+
56+
$newimage = wp_imagecreatetruecolor( $dst_w, $dst_h );
57+
58+
imagecopyresampled( $newimage, $this->image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h );
59+
60+
// convert from full colors to index colors, like original PNG.
61+
if ( IMAGETYPE_PNG == $this->orig_type && function_exists('imageistruecolor') && !imageistruecolor( $this->image ) )
62+
imagetruecolortopalette( $newimage, false, imagecolorstotal( $this->image ) );
63+
64+
// $suffix will be appended to the destination filename, just before the extension
65+
if ( ! $suffix )
66+
$suffix = "{$dst_w}x{$dst_h}";
67+
68+
$info = pathinfo( $this->file );
69+
$dir = $info['dirname'];
70+
$ext = $info['extension'];
71+
$name = wp_basename( $this->file, ".$ext" );
72+
73+
if ( ! is_null( $dest_path ) && $_dest_path = realpath( $dest_path ) )
74+
$dir = $_dest_path;
75+
$destfilename = "{$dir}/{$name}-{$suffix}.{$ext}";
76+
77+
if ( IMAGETYPE_GIF == $this->orig_type ) {
78+
if ( ! imagegif( $newimage, $destfilename ) )
79+
return new WP_Error( 'resize_path_invalid', __( 'Resize path invalid' ) );
80+
}
81+
elseif ( IMAGETYPE_PNG == $this->orig_type ) {
82+
if ( !imagepng( $newimage, $destfilename ) )
83+
return new WP_Error( 'resize_path_invalid', __( 'Resize path invalid' ) );
84+
}
85+
else {
86+
// all other formats are converted to jpg
87+
if ( 'jpg' != $ext && 'jpeg' != $ext )
88+
$destfilename = "{$dir}/{$name}-{$suffix}.jpg";
89+
90+
if ( ! imagejpeg( $newimage, $destfilename, apply_filters( 'jpeg_quality', $jpeg_quality, 'image_resize' ) ) )
91+
return new WP_Error( 'resize_path_invalid', __( 'Resize path invalid' ) );
92+
}
93+
94+
imagedestroy( $newimage );
95+
96+
// Set correct file permissions
97+
$stat = stat( dirname( $destfilename ) );
98+
$perms = $stat['mode'] & 0000666; //same permissions as parent folder, strip off the executable bits
99+
@ chmod( $destfilename, $perms );
100+
101+
return array(
102+
'path' => $destfilename,
103+
'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $destfilename ) ),
104+
'width' => $dst_w,
105+
'height' => $dst_h
106+
);
107+
}
108+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
class WP_Image_Editor_Imagick extends WP_Image_Editor_Base {
4+
private $image = false;
5+
6+
public static function test() {
7+
if ( ! extension_loaded('imagick') )
8+
return false;
9+
10+
return true;
11+
}
12+
13+
private function load() {
14+
if ( ! file_exists( $this->file ) )
15+
return sprintf( __('File &#8220;%s&#8221; doesn&#8217;t exist?'), $this->file );
16+
17+
try {
18+
$this->image = new Imagick( $this->file );
19+
}
20+
catch ( Exception $e ) {
21+
return sprintf(__('File &#8220;%s&#8221; is not an image.'), $this->file);
22+
}
23+
24+
if( ! $this->image->valid() ) {
25+
return sprintf(__('File &#8220;%s&#8221; is not an image.'), $this->file);
26+
}
27+
28+
$this->size = $this->image->getImageGeometry();
29+
$this->orig_type = $this->image->getImageFormat();
30+
if ( ! $this->size )
31+
return new WP_Error( 'invalid_image', __('Could not read image size'), $this->file );
32+
33+
return true;
34+
}
35+
36+
public function resize( $max_w, $max_h, $crop = false, $suffix = null, $dest_path = null, $jpeg_quality = 90 ) {
37+
// Yes, this is forcing a load every time at the moment.
38+
// However, for multi-resize to work, it needs to do so, unless it's going to resize based on a modified image.
39+
if ( ! $this->load() )
40+
return false;
41+
42+
if ( ! is_object( $this->image ) )
43+
return new WP_Error( 'error_loading_image', $this->image, $this->file );
44+
45+
$dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop );
46+
if ( ! $dims )
47+
return new WP_Error( 'error_getting_dimensions', __('Could not calculate resized image dimensions') );
48+
list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims;
49+
50+
if( 'JPEG' == $this->orig_type ) {
51+
$this->image->setImageCompression( imagick::COMPRESSION_JPEG );
52+
$this->image->setImageCompressionQuality( $jpeg_quality );
53+
}
54+
55+
if ( $crop ) {
56+
$this->image->cropImage( $src_w, $src_h, $src_x, $src_y );
57+
}
58+
59+
//$this->image->thumbnailImage( $dst_w, $dst_h );
60+
$this->image->scaleImage( $dst_w, $dst_h, true );
61+
62+
// $suffix will be appended to the destination filename, just before the extension
63+
if ( ! $suffix )
64+
$suffix = "{$dst_w}x{$dst_h}";
65+
66+
$info = pathinfo( $this->file );
67+
$dir = $info['dirname'];
68+
$ext = $info['extension'];
69+
$name = wp_basename( $this->file, ".$ext" );
70+
71+
if ( ! is_null( $dest_path ) && $_dest_path = realpath( $dest_path ) )
72+
$dir = $_dest_path;
73+
$destfilename = "{$dir}/{$name}-{$suffix}.{$ext}";
74+
75+
if( apply_filters( 'wp_editors_stripimage', true ) ) {
76+
$this->image->stripImage();
77+
}
78+
79+
$this->image->writeImage( $destfilename );
80+
81+
// Set correct file permissions
82+
$stat = stat( dirname( $destfilename ) );
83+
$perms = $stat['mode'] & 0000666; //same permissions as parent folder, strip off the executable bits
84+
@ chmod( $destfilename, $perms );
85+
86+
return array(
87+
'path' => $destfilename,
88+
'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $destfilename ) ),
89+
'width' => $dst_w,
90+
'height' => $dst_h
91+
);
92+
}
93+
}

0 commit comments

Comments
 (0)