Ticket #6821: 6821.initialImagickSupport.diff

File 6821.initialImagickSupport.diff, 46.7 KB (added by DH-Shredder, 9 months ago)

Initial Imagick support (module)

Line 
1diff --git wp-admin/includes/image-edit.php wp-admin/includes/image-edit.php
2index d2d6dc2..da40ab2 100644
3--- wp-admin/includes/image-edit.php
4+++ wp-admin/includes/image-edit.php
5@@ -197,39 +197,80 @@ function wp_image_editor($post_id, $msg = false) {
6 <?php
7 }
8 
9-function wp_stream_image($image, $mime_type, $post_id) {
10-       $image = apply_filters('image_save_pre', $image, $post_id);
11-
12-       switch ( $mime_type ) {
13-               case 'image/jpeg':
14-                       header('Content-Type: image/jpeg');
15-                       return imagejpeg($image, null, 90);
16-               case 'image/png':
17-                       header('Content-Type: image/png');
18-                       return imagepng($image);
19-               case 'image/gif':
20-                       header('Content-Type: image/gif');
21-                       return imagegif($image);
22-               default:
23-                       return false;
24-       }
25+/**
26+ * Streams image in WP_Image_Editor to browser.
27+ * Provided for backcompat reasons
28+ *
29+ * @param WP_Image_Editor $image
30+ * @param string $mime_type
31+ * @param int $post_id
32+ * @return boolean
33+ */
34+function wp_stream_image( $image, $mime_type, $post_id ) {
35+       $image = apply_filters('image_editor_save_pre', $image, $post_id);
36+
37+       if ( ! is_resource( $image ) ) {
38+               $image->stream();
39+
40+    } else {
41+               _deprecated_argument( __FUNCTION__, '3.5', __( '$image needs to be an WP_Image_Editor object' ) );
42+
43+               $image = apply_filters('image_save_pre', $image, $post_id);
44+
45+               switch ( $mime_type ) {
46+                   case 'image/jpeg':
47+                           header( 'Content-Type: image/jpeg' );
48+                           return imagejpeg( $image, null, 90 );
49+                   case 'image/png':
50+                           header( 'Content-Type: image/png' );
51+                           return imagepng( $image );
52+                   case 'image/gif':
53+                           header( 'Content-Type: image/gif' );
54+                           return imagegif( $image );
55+                   default:
56+                               return false;
57+               }
58+    }
59 }
60 
61-function wp_save_image_file($filename, $image, $mime_type, $post_id) {
62-       $image = apply_filters('image_save_pre', $image, $post_id);
63-       $saved = apply_filters('wp_save_image_file', null, $filename, $image, $mime_type, $post_id);
64-       if ( null !== $saved )
65-               return $saved;
66-
67-       switch ( $mime_type ) {
68-               case 'image/jpeg':
69-                       return imagejpeg( $image, $filename, apply_filters( 'jpeg_quality', 90, 'edit_image' ) );
70-               case 'image/png':
71-                       return imagepng($image, $filename);
72-               case 'image/gif':
73-                       return imagegif($image, $filename);
74-               default:
75-                       return false;
76+/**
77+ * @TODO: Public function that accepts GD images as input.
78+ * @TODO: Add mime_type support to WP_Image_Editor
79+ *
80+ * @param string $filename
81+ * @param WP_Image_Editor $image
82+ * @param string $mime_type
83+ * @param int $post_id
84+ * @return boolean
85+ */
86+function wp_save_image_file( $filename, $image, $mime_type, $post_id ) {
87+       if ( ! is_resource( $image ) ) {
88+               $image = apply_filters('image_editor_save_pre', $image, $post_id);
89+               $saved = apply_filters('wp_save_image_editor_file', null, $filename, $image, $mime_type, $post_id);
90+
91+               if ( null !== $saved )
92+                       return $saved;
93+
94+               return $image->save( $filename );
95+       } else {
96+               _deprecated_argument( __FUNCTION__, '3.5', __( '$image needs to be an WP_Image_Editor object' ) );
97+
98+               $image = apply_filters('image_save_pre', $image, $post_id);
99+               $saved = apply_filters('wp_save_image_file', null, $filename, $image, $mime_type, $post_id);
100+
101+               if ( null !== $saved )
102+                       return $saved;
103+
104+               switch ( $mime_type ) {
105+                       case 'image/jpeg':
106+                               return imagejpeg( $image, $filename, apply_filters( 'jpeg_quality', 90, 'edit_image' ) );
107+                       case 'image/png':
108+                               return imagepng( $image, $filename );
109+                       case 'image/gif':
110+                               return imagegif( $image, $filename );
111+                       default:
112+                               return false;
113+               }
114        }
115 }
116 
117@@ -238,6 +279,7 @@ function _image_get_preview_ratio($w, $h) {
118        return $max > 400 ? (400 / $max) : 1;
119 }
120 
121+// @TODO: Returns GD resource, but is NOT public
122 function _rotate_image_resource($img, $angle) {
123        if ( function_exists('imagerotate') ) {
124                $rotated = imagerotate($img, $angle, 0);
125@@ -249,6 +291,16 @@ function _rotate_image_resource($img, $angle) {
126        return $img;
127 }
128 
129+/**
130+ * @TODO: Only used within image_edit_apply_changes
131+ *               and receives/returns GD Resource.
132+ *               Consider removal.
133+ *
134+ * @param GD_Resource $img
135+ * @param boolean $horz
136+ * @param boolean $vert
137+ * @return GD_Resource
138+ */
139 function _flip_image_resource($img, $horz, $vert) {
140        $w = imagesx($img);
141        $h = imagesy($img);
142@@ -267,6 +319,18 @@ function _flip_image_resource($img, $horz, $vert) {
143        return $img;
144 }
145 
146+/**
147+ * @TODO: Only used within image_edit_apply_changes
148+ *               and receives/returns GD Resource.
149+ *               Consider removal.
150+ *
151+ * @param GD_Resource $img
152+ * @param float $x
153+ * @param float $y
154+ * @param float $w
155+ * @param float $h
156+ * @return GD_Resource
157+ */
158 function _crop_image_resource($img, $x, $y, $w, $h) {
159        $dst = wp_imagecreatetruecolor($w, $h);
160        if ( is_resource($dst) ) {
161@@ -278,10 +342,19 @@ function _crop_image_resource($img, $x, $y, $w, $h) {
162        return $img;
163 }
164 
165-function image_edit_apply_changes($img, $changes) {
166+/**
167+ * Performs group of changes on Editor specified.
168+ *
169+ * @param WP_Image_Editor $image
170+ * @param type $changes
171+ * @return WP_Image_Editor
172+ */
173+function image_edit_apply_changes( $image, $changes ) {
174+       if ( is_resource( $image ) )
175+               _deprecated_argument( __FUNCTION__, '3.5', __( '$image needs to be an WP_Image_Editor object' ) );
176 
177        if ( !is_array($changes) )
178-               return $img;
179+               return $image;
180 
181        // expand change operations
182        foreach ( $changes as $key => $obj ) {
183@@ -326,54 +399,81 @@ function image_edit_apply_changes($img, $changes) {
184        }
185 
186        // image resource before applying the changes
187-       $img = apply_filters('image_edit_before_change', $img, $changes);
188+       if ( ! is_resource( $image ) )
189+               $image = apply_filters('wp_image_editor_before_change', $image, $changes);
190+       else
191+               $image = apply_filters('image_edit_before_change', $image, $changes);
192 
193        foreach ( $changes as $operation ) {
194                switch ( $operation->type ) {
195                        case 'rotate':
196-                               if ( $operation->angle != 0 )
197-                                       $img = _rotate_image_resource($img, $operation->angle);
198+                               if ( $operation->angle != 0 ) {
199+                                       if ( ! is_resource( $image ) )
200+                                               $image->rotate( $operation->angle );
201+                                       else
202+                                               $image = _rotate_image_resource( $image, $operation->angle );
203+                               }
204                                break;
205                        case 'flip':
206                                if ( $operation->axis != 0 )
207-                                       $img = _flip_image_resource($img, ($operation->axis & 1) != 0, ($operation->axis & 2) != 0);
208+                                       if ( ! is_resource( $image ) )
209+                                               $image->flip( ($operation->axis & 1) != 0, ($operation->axis & 2) != 0 );
210+                                       else
211+                                               $image = _flip_image_resource( $image, ( $operation->axis & 1 ) != 0, ( $operation->axis & 2 ) != 0 );
212                                break;
213                        case 'crop':
214                                $sel = $operation->sel;
215-                               $scale = 1 / _image_get_preview_ratio( imagesx($img), imagesy($img) ); // discard preview scaling
216-                               $img = _crop_image_resource($img, $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale);
217+
218+                               if ( ! is_resource( $image ) ) {
219+                                       $size = $image->get_size();
220+                                       $w = $size['width'];
221+                                       $h = $size['height'];
222+
223+                                       $scale = 1 / _image_get_preview_ratio( $w, $h ); // discard preview scaling
224+                                       $image->crop( $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale );
225+                               } else {
226+                                       $scale = 1 / _image_get_preview_ratio( imagesx( $image ), imagesy( $image ) ); // discard preview scaling
227+                                       $image = _crop_image_resource( $image, $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale );
228+                               }
229                                break;
230                }
231        }
232 
233-       return $img;
234+       return $image;
235 }
236 
237+
238+/**
239+ * Streams image in post to browser, along with enqueued changes
240+ * in $_REQUEST['history']
241+ *
242+ * @param int $post_id
243+ * @return boolean
244+ */
245 function stream_preview_image($post_id) {
246        $post = get_post($post_id);
247        @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
248-       $img = load_image_to_edit( $post_id, $post->post_mime_type, array(400, 400) );
249 
250-       if ( !is_resource($img) )
251-               return false;
252+       $img = WP_Image_Editor::get_instance( _load_image_to_edit_path( $post_id ) );
253+
254+    if ( ! $img )
255+        return false;
256 
257        $changes = !empty($_REQUEST['history']) ? json_decode( stripslashes($_REQUEST['history']) ) : null;
258        if ( $changes )
259-               $img = image_edit_apply_changes($img, $changes);
260+               $img = image_edit_apply_changes( $img, $changes );
261 
262        // scale the image
263-       $w = imagesx($img);
264-       $h = imagesy($img);
265-       $ratio = _image_get_preview_ratio($w, $h);
266+       $size = $img->get_size();
267+       $w = $size['width'];
268+       $h = $size['height'];
269+
270+       $ratio = _image_get_preview_ratio( $w, $h );
271        $w2 = $w * $ratio;
272        $h2 = $h * $ratio;
273 
274-       $preview = wp_imagecreatetruecolor($w2, $h2);
275-       imagecopyresampled( $preview, $img, 0, 0, 0, 0, $w2, $h2, $w, $h );
276-       wp_stream_image($preview, $post->post_mime_type, $post_id);
277-
278-       imagedestroy($preview);
279-       imagedestroy($img);
280+       $img->resize( $w2, $h2 );
281+       wp_stream_image( $img, $post->post_mime_type, $post_id );
282        return true;
283 }
284 
285@@ -452,14 +552,20 @@ function wp_restore_image($post_id) {
286        return $msg;
287 }
288 
289-function wp_save_image($post_id) {
290+/**
291+ * Saves image to post along with enqueued changes
292+ * in $_REQUEST['history']
293+ *
294+ * @param int $post_id
295+ * @return \stdClass
296+ */
297+function wp_save_image( $post_id ) {
298        $return = new stdClass;
299        $success = $delete = $scaled = $nocrop = false;
300-       $post = get_post($post_id);
301-       @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
302-       $img = load_image_to_edit($post_id, $post->post_mime_type);
303+       $post = get_post( $post_id );
304 
305-       if ( !is_resource($img) ) {
306+       $img = WP_Image_Editor::get_instance( _load_image_to_edit_path( $post_id, 'full' ) );
307+       if ( !$img ) {
308                $return->error = esc_js( __('Unable to create new image.') );
309                return $return;
310        }
311@@ -470,19 +576,16 @@ function wp_save_image($post_id) {
312        $scale = !empty($_REQUEST['do']) && 'scale' == $_REQUEST['do'];
313 
314        if ( $scale && $fwidth > 0 && $fheight > 0 ) {
315-               $sX = imagesx($img);
316-               $sY = imagesy($img);
317+               $size = $img->get_size();
318+               $sX = $size['width'];
319+               $sY = $size['height'];
320 
321                // check if it has roughly the same w / h ratio
322                $diff = round($sX / $sY, 2) - round($fwidth / $fheight, 2);
323                if ( -0.1 < $diff && $diff < 0.1 ) {
324                        // scale the full size image
325-                       $dst = wp_imagecreatetruecolor($fwidth, $fheight);
326-                       if ( imagecopyresampled( $dst, $img, 0, 0, 0, 0, $fwidth, $fheight, $sX, $sY ) ) {
327-                               imagedestroy($img);
328-                               $img = $dst;
329+                       if ( $img->resize( $fwidth, $fheight ) )
330                                $scaled = true;
331-                       }
332                }
333 
334                if ( !$scaled ) {
335@@ -553,11 +656,13 @@ function wp_save_image($post_id) {
336                if ( $tag )
337                        $backup_sizes[$tag] = array('width' => $meta['width'], 'height' => $meta['height'], 'file' => $path_parts['basename']);
338 
339-               $success = update_attached_file($post_id, $new_path);
340+               $success = update_attached_file( $post_id, $new_path );
341+
342+               $meta['file'] = _wp_relative_upload_path( $new_path );
343 
344-               $meta['file'] = _wp_relative_upload_path($new_path);
345-               $meta['width'] = imagesx($img);
346-               $meta['height'] = imagesy($img);
347+               $size = $img->get_size();
348+               $meta['width'] = $size['width'];
349+               $meta['height'] = $size['height'];
350 
351                list ( $uwidth, $uheight ) = wp_constrain_dimensions($meta['width'], $meta['height'], 128, 96);
352                $meta['hwstring_small'] = "height='$uheight' width='$uwidth'";
353@@ -575,10 +680,12 @@ function wp_save_image($post_id) {
354                $success = $delete = $nocrop = true;
355        }
356 
357-       if ( isset($sizes) ) {
358+       if ( isset( $sizes ) ) {
359+               $_sizes = array();
360+
361                foreach ( $sizes as $size ) {
362                        $tag = false;
363-                       if ( isset($meta['sizes'][$size]) ) {
364+                       if ( isset( $meta['sizes'][$size] ) ) {
365                                if ( isset($backup_sizes["$size-orig"]) ) {
366                                        if ( ( !defined('IMAGE_EDIT_OVERWRITE') || !IMAGE_EDIT_OVERWRITE ) && $backup_sizes["$size-orig"]['file'] != $meta['sizes'][$size]['file'] )
367                                                $tag = "$size-$suffix";
368@@ -591,17 +698,16 @@ function wp_save_image($post_id) {
369                        }
370 
371                        $crop = $nocrop ? false : get_option("{$size}_crop");
372-                       $resized = image_make_intermediate_size($new_path, get_option("{$size}_size_w"), get_option("{$size}_size_h"), $crop );
373-
374-                       if ( $resized )
375-                               $meta['sizes'][$size] = $resized;
376-                       else
377-                               unset($meta['sizes'][$size]);
378+                       $_sizes[ $size ] = array( 'width' => get_option("{$size}_size_w"), 'height' => get_option("{$size}_size_h"), 'crop' => $crop );
379                }
380+
381+               $meta['sizes'] = $img->multi_resize( $_sizes );
382        }
383 
384+       unset( $img );
385+
386        if ( $success ) {
387-               wp_update_attachment_metadata($post_id, $meta);
388+               wp_update_attachment_metadata( $post_id, $meta );
389                update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes);
390 
391                if ( $target == 'thumbnail' || $target == 'all' || $target == 'full' ) {
392@@ -617,11 +723,9 @@ function wp_save_image($post_id) {
393 
394        if ( $delete ) {
395                $delpath = apply_filters('wp_delete_file', $new_path);
396-               @unlink($delpath);
397+               @unlink( $delpath );
398        }
399 
400-       imagedestroy($img);
401-
402        $return->msg = esc_js( __('Image saved') );
403        return $return;
404 }
405diff --git wp-admin/includes/image.php wp-admin/includes/image.php
406index 8048387..6dbc39f 100644
407--- wp-admin/includes/image.php
408+++ wp-admin/includes/image.php
409@@ -43,61 +43,33 @@ function wp_create_thumbnail( $file, $max_side, $deprecated = '' ) {
410  * @param string $dst_file Optional. The destination file to write to.
411  * @return string|WP_Error|false New filepath on success, WP_Error or false on failure.
412  */
413-function wp_crop_image( $src, $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs = false, $dst_file = false ) {
414-       if ( is_numeric( $src ) ) { // Handle int as attachment ID
415-               $src_file = get_attached_file( $src );
416+function wp_crop_image( $src_file, $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs = false, $dst_file = false ) {
417+       if ( is_numeric( $src_file ) ) { // Handle int as attachment ID
418+               $src_file = get_attached_file( $src_file );
419                if ( ! file_exists( $src_file ) ) {
420                        // If the file doesn't exist, attempt a url fopen on the src link.
421                        // This can occur with certain file replication plugins.
422-                       $post = get_post( $src );
423-                       $image_type = $post->post_mime_type;
424-                       $src = load_image_to_edit( $src, $post->post_mime_type, 'full' );
425-               } else {
426-                       $size = @getimagesize( $src_file );
427-                       $image_type = ( $size ) ? $size['mime'] : '';
428-                       $src = wp_load_image( $src_file );
429+                       $src_file = _load_image_to_edit_path( $src_file, 'full' );
430                }
431-       } else {
432-               $size = @getimagesize( $src );
433-               $image_type = ( $size ) ? $size['mime'] : '';
434-               $src = wp_load_image( $src );
435-       }
436-
437-       if ( ! is_resource( $src ) )
438-               return new WP_Error( 'error_loading_image', $src, $src_file );
439-
440-       $dst = wp_imagecreatetruecolor( $dst_w, $dst_h );
441-
442-       if ( $src_abs ) {
443-               $src_w -= $src_x;
444-               $src_h -= $src_y;
445        }
446 
447-       if ( function_exists( 'imageantialias' ) )
448-               imageantialias( $dst, true );
449+       $editor = WP_Image_Editor::get_instance( $src_file );
450+       $src = $editor->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs );
451 
452-       imagecopyresampled( $dst, $src, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h );
453-
454-       imagedestroy( $src ); // Free up memory
455+       if ( ! $src )
456+               return new WP_Error( 'error_loading_image', '', $src_file );
457 
458        if ( ! $dst_file )
459                $dst_file = str_replace( basename( $src_file ), 'cropped-' . basename( $src_file ), $src_file );
460 
461-       if ( 'image/png' != $image_type )
462-               $dst_file = preg_replace( '/\\.[^\\.]+$/', '.jpg', $dst_file );
463-
464        // The directory containing the original file may no longer exist when
465        // using a replication plugin.
466        wp_mkdir_p( dirname( $dst_file ) );
467 
468        $dst_file = dirname( $dst_file ) . '/' . wp_unique_filename( dirname( $dst_file ), basename( $dst_file ) );
469 
470-       if ( 'image/png' == $image_type && imagepng( $dst, $dst_file ) )
471-               return $dst_file;
472-       elseif ( imagejpeg( $dst, $dst_file, apply_filters( 'jpeg_quality', 90, 'wp_crop_image' ) ) )
473-               return $dst_file;
474-       else
475-               return false;
476+       $result = $editor->save( $dst_file );
477+       return $result['file'];
478 }
479 
480 /**
481@@ -144,11 +116,9 @@ function wp_generate_attachment_metadata( $attachment_id, $file ) {
482 
483                $sizes = apply_filters( 'intermediate_image_sizes_advanced', $sizes );
484 
485-               foreach ($sizes as $size => $size_data ) {
486-                       $resized = image_make_intermediate_size( $file, $size_data['width'], $size_data['height'], $size_data['crop'] );
487-                       if ( $resized )
488-                               $metadata['sizes'][$size] = $resized;
489-               }
490+               $editor = WP_Image_Editor::get_instance( $file );
491+               $metadata['sizes'] = $editor->multi_resize( $sizes );
492+               unset( $editor );
493 
494                // fetch additional metadata from exif/iptc
495                $image_meta = wp_read_image_metadata( $file );
496diff --git wp-includes/class-wp-image-editor.php wp-includes/class-wp-image-editor.php
497new file mode 100644
498index 0000000..f499bf9
499--- /dev/null
500+++ wp-includes/class-wp-image-editor.php
501@@ -0,0 +1,47 @@
502+<?php
503+
504+class WP_Image_Editor {
505+
506+       final public static function get_instance( $path ) {
507+               $implementation = apply_filters( 'image_editor_class', self::choose_implementation(), $path );
508+
509+               if ( $implementation )
510+                       return new $implementation( $path );
511+
512+               return false;
513+       }
514+
515+       /**
516+        * Tests which editors are capable of supporting the request.
517+        *
518+        * @since 3.5.0
519+        * @access private
520+        *
521+        * @return string|bool Class name for the first editor that claims to support the request. False if no editor claims to support the request.
522+        */
523+       private static function choose_implementation() {
524+               static $implementation;
525+
526+               if ( null === $implementation ) {
527+                       $request_order = apply_filters( 'wp_editors', array( 'imagick', 'gd' ) );
528+
529+                       // Loop over each editor on each request looking for one which will serve this request's needs
530+                       foreach ( $request_order as $editor ) {
531+                               $class = 'WP_Image_Editor_' . $editor;
532+
533+                               // Check to see if this editor is a possibility, calls the editor statically
534+                               if ( ! call_user_func( array( $class, 'test' ) ) )
535+                                       continue;
536+
537+                               if( ! apply_filters( 'wp_editor_use_' . $editor, true ) )
538+                                       continue;
539+
540+                               $implementation = $class;
541+
542+                               break;
543+                       }
544+               }
545+
546+               return $implementation;
547+       }
548+}
549\ No newline at end of file
550diff --git wp-includes/deprecated.php wp-includes/deprecated.php
551index da8bb1d..3386fd7 100644
552--- wp-includes/deprecated.php
553+++ wp-includes/deprecated.php
554@@ -3206,6 +3206,73 @@ function _get_post_ancestors( &$post ) {
555 }
556 
557 /**
558+ * Load an image from a string, if PHP supports it.
559+ *
560+ * @since 2.1.0
561+ * @deprecated 3.5.0
562+ * @deprecated wp_get_image_for_editing()
563+ *
564+ * @param string $file Filename of the image to load.
565+ * @return resource The resulting image resource on success, Error string on failure.
566+ */
567+function wp_load_image( $file ) {
568+       _deprecated_function( __FUNCTION__, '3.5', 'wp_get_image_for_editing()' );
569+
570+       if ( is_numeric( $file ) )
571+               $file = get_attached_file( $file );
572+
573+       if ( ! file_exists( $file ) )
574+               return sprintf(__('File &#8220;%s&#8221; doesn&#8217;t exist?'), $file);
575+
576+       if ( ! function_exists('imagecreatefromstring') )
577+               return __('The GD image library is not installed.');
578+
579+       // Set artificially high because GD uses uncompressed images in memory
580+       @ini_set( 'memory_limit', apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT ) );
581+       $image = imagecreatefromstring( file_get_contents( $file ) );
582+
583+       if ( !is_resource( $image ) )
584+               return sprintf(__('File &#8220;%s&#8221; is not an image.'), $file);
585+
586+       return $image;
587+}
588+
589+/**
590+ * Scale down an image to fit a particular size and save a new copy of the image.
591+ *
592+ * The PNG transparency will be preserved using the function, as well as the
593+ * image type. If the file going in is PNG, then the resized image is going to
594+ * be PNG. The only supported image types are PNG, GIF, and JPEG.
595+ *
596+ * Some functionality requires API to exist, so some PHP version may lose out
597+ * support. This is not the fault of WordPress (where functionality is
598+ * downgraded, not actual defects), but of your PHP version.
599+ *
600+ * @since 2.5.0
601+ * @deprecated 3.5.0
602+ * @deprecated wp_get_image_for_editing()
603+ *
604+ * @param string $file Image file path.
605+ * @param int $max_w Maximum width to resize to.
606+ * @param int $max_h Maximum height to resize to.
607+ * @param bool $crop Optional. Whether to crop image or resize.
608+ * @param string $suffix Optional. File suffix.
609+ * @param string $dest_path Optional. New image file path.
610+ * @param int $jpeg_quality Optional, default is 90. Image quality percentage.
611+ * @return mixed WP_Error on failure. String with new destination path.
612+ */
613+function image_resize( $file, $max_w, $max_h, $crop = false, $suffix = null, $dest_path = null, $jpeg_quality = 90 ) {
614+       _deprecated_function( __FUNCTION__, '3.5', 'wp_get_image_for_editing()' );
615+
616+       $editor = new WP_Image_Editor_GD( $file );
617+       $editor->set_quality( $jpeg_quality );
618+
619+       $editor->resize( $max_w, $max_h, $crop );
620+       $editor->save( $editor->generate_filename( $suffix, $dest_path ) );
621+       unset( $editor );
622+}
623+
624+/**
625  * Retrieve a single post, based on post ID.
626  *
627  * Has categories in 'post_category' property or key. Has tags in 'tags_input'
628diff --git wp-includes/editors/class-wp-image-editor-base.php wp-includes/editors/class-wp-image-editor-base.php
629new file mode 100644
630index 0000000..ff85ba0
631--- /dev/null
632+++ wp-includes/editors/class-wp-image-editor-base.php
633@@ -0,0 +1,53 @@
634+<?php
635+
636+class WP_Image_Editor_Base {
637+       protected $file = false;
638+       protected $size = false;
639+       protected $orig_type  = false;
640+       protected $quality = 90;
641+
642+       function __construct( $filename ) {
643+               $this->file = $filename;
644+       }
645+
646+       public static function test() {
647+               return false;
648+       }
649+
650+       public function get_size() {
651+               return $this->size;
652+       }
653+
654+       protected function update_size( $width = false, $height = false ) {
655+               $this->size = array(
656+                       'width' => $width,
657+                       'height' => $height
658+               );
659+       }
660+
661+       public function set_quality( $quality ) {
662+               $this->quality = apply_filters( 'wp_editor_set_quality', $quality );
663+       }
664+
665+       public function generate_filename( $suffix = null, $dest_path = null ) {
666+               // $suffix will be appended to the destination filename, just before the extension
667+               $suffix = $this->get_suffix();
668+
669+               $info = pathinfo( $this->file );
670+               $dir  = $info['dirname'];
671+               $ext  = $info['extension'];
672+               $name = wp_basename( $this->file, ".$ext" );
673+
674+               if ( ! is_null( $dest_path ) && $_dest_path = realpath( $dest_path ) )
675+                       $dir = $_dest_path;
676+
677+               return "{$dir}/{$name}-{$suffix}.{$ext}";
678+       }
679+
680+       public function get_suffix() {
681+               if ( ! $this->get_size() )
682+                       return;
683+
684+               return "{$this->size['width']}x{$this->size['height']}";
685+       }
686+}
687\ No newline at end of file
688diff --git wp-includes/editors/class-wp-image-editor-gd.php wp-includes/editors/class-wp-image-editor-gd.php
689new file mode 100644
690index 0000000..3dc9c0f
691--- /dev/null
692+++ wp-includes/editors/class-wp-image-editor-gd.php
693@@ -0,0 +1,349 @@
694+<?php
695+
696+class WP_Image_Editor_GD extends WP_Image_Editor_Base {
697+       private $image = false; // GD Resource
698+
699+       function __destruct() {
700+               if ( $this->image ) {
701+                       // we don't need the original in memory anymore
702+                       imagedestroy( $this->image );
703+               }
704+       }
705+
706+       /**
707+        * Checks to see if GD is available.
708+        *
709+        * @return boolean
710+        */
711+       public static function test() {
712+               if ( ! extension_loaded('gd') || ! function_exists('gd_info') )
713+                       return false;
714+
715+               return true;
716+       }
717+
718+       /**
719+        * Loads image from $this->file into GD Resource
720+        *
721+        * @since 3.5
722+        *
723+        * @return boolean|\WP_Error
724+        */
725+       protected function load() {
726+               if ( $this->image )
727+                       return true;
728+
729+               if ( ! file_exists( $this->file ) )
730+                       return sprintf( __('File &#8220;%s&#8221; doesn&#8217;t exist?'), $this->file );
731+
732+               if ( ! function_exists('imagecreatefromstring') )
733+                       return __('The GD image library is not installed.');
734+
735+               // Set artificially high because GD uses uncompressed images in memory
736+               @ini_set( 'memory_limit', apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT ) );
737+               $this->image = imagecreatefromstring( file_get_contents( $this->file ) );
738+
739+               if ( ! is_resource( $this->image ) )
740+                       return sprintf( __('File &#8220;%s&#8221; is not an image.'), $this->file );
741+
742+               $size = @getimagesize( $this->file );
743+               if ( ! $size )
744+                       return new WP_Error( 'invalid_image', __('Could not read image size'), $this->file );
745+
746+               $this->update_size( $size[0], $size[1] );
747+               $this->orig_type = $size['mime'];
748+
749+               return true;
750+       }
751+
752+       public function get_size() {
753+               if ( ! $this->load() )
754+                       return;
755+
756+               return parent::get_size();
757+       }
758+
759+       protected function update_size( $width = false, $height = false ) {
760+               if ( ! $this->load() )
761+                       return;
762+
763+               parent::update_size( $width ?: imagesx( $this->image ), $height ?: imagesy( $this->image ) );
764+       }
765+
766+       public function resize( $max_w, $max_h, $crop = false ) {
767+               $resized = $this->_resize( $max_w, $max_h, $crop );
768+
769+               if ( is_resource( $resized ) ) {
770+                       imagedestroy( $this->image );
771+                       $this->image = $resized;
772+
773+                       return true;
774+               }
775+
776+               return $resized;
777+       }
778+
779+       protected function _resize( $max_w, $max_h, $crop ) {
780+               if ( ! $this->load() )
781+                       return;
782+
783+               $dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop );
784+               if ( ! $dims ) {
785+                       return new WP_Error( 'error_getting_dimensions', __('Could not calculate resized image dimensions') );
786+               }
787+               list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims;
788+
789+               $resized = wp_imagecreatetruecolor( $dst_w, $dst_h );
790+               imagecopyresampled( $resized, $this->image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h );
791+
792+               if ( is_resource( $resized ) ) {
793+                       $this->update_size( $dst_w, $dst_h );
794+                       return $resized;
795+               }
796+
797+               return false;
798+       }
799+
800+       /**
801+        * Processes current image and saves to disk
802+        * multiple sizes from single source.
803+        *
804+        * @param array $sizes
805+        * @return array
806+        */
807+       public function multi_resize( $sizes ) {
808+               $metadata = array();
809+               if ( ! $this->load() )
810+                       return $metadata;
811+
812+               $orig_size = $this->size;
813+               foreach ( $sizes as $size => $size_data ) {
814+                       $image = $this->_resize( $size_data['width'], $size_data['height'], $size_data['crop'] );
815+
816+                       if( ! is_wp_error( $image ) ) {
817+                               $resized = $this->_save( $image );
818+
819+                               imagedestroy( $image );
820+                               unset( $resized['path'] );
821+
822+                               if ( ! is_wp_error( $resized ) && $resized )
823+                                       $metadata[$size] = $resized;
824+                       }
825+
826+                       $this->size = $orig_size;
827+               }
828+
829+               return $metadata;
830+       }
831+
832+       /**
833+        * Ported from image.php
834+        *
835+        * @param float $x
836+        * @param float $y
837+        * @param float $w
838+        * @param float $h
839+        * @return boolean
840+        */
841+       public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) {
842+               if ( ! $this->load() )
843+                       return;
844+
845+               // If destination width/height isn't specified, use same as
846+               // width/height from source.
847+               $dst_w = $dst_w ?: $src_w;
848+               $dst_h = $dst_h ?: $src_h;
849+               $dst = wp_imagecreatetruecolor( $dst_w, $dst_h );
850+
851+               if ( $src_abs ) {
852+                       $src_w -= $src_x;
853+                       $src_h -= $src_y;
854+               }
855+
856+               if ( function_exists( 'imageantialias' ) )
857+                       imageantialias( $dst, true );
858+
859+               imagecopyresampled( $dst, $this->image, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h );
860+
861+               if ( is_resource( $dst ) ) {
862+                       imagedestroy( $this->image );
863+                       $this->image = $dst;
864+                       $this->update_size( $dst_w, $dst_h );
865+                       return true;
866+               }
867+
868+               return false; // @TODO: WP_Error here.
869+       }
870+
871+       /**
872+        * Rotates in memory image by $angle.
873+        * Ported from image-edit.php
874+        *
875+        * @param float $angle
876+        * @return boolean
877+        */
878+       public function rotate( $angle ) {
879+               if ( ! $this->load() )
880+                       return;
881+
882+               if ( function_exists('imagerotate') ) {
883+                       $rotated = imagerotate( $this->image, $angle, 0 );
884+
885+                       if ( is_resource( $rotated ) ) {
886+                               imagedestroy( $this->image );
887+                               $this->image = $rotated;
888+                               $this->update_size();
889+                               return true;
890+                       }
891+               }
892+               return false; // @TODO: WP_Error here.
893+       }
894+
895+       /**
896+        * Ported from image-edit.php
897+        *
898+        * @param boolean $horz
899+        * @param boolean $vert
900+        */
901+       public function flip( $horz, $vert ) {
902+               if ( ! $this->load() )
903+                       return;
904+
905+               $w = $this->size['width'];
906+               $h = $this->size['height'];
907+               $dst = wp_imagecreatetruecolor( $w, $h );
908+
909+               if ( is_resource( $dst ) ) {
910+                       $sx = $vert ? ($w - 1) : 0;
911+                       $sy = $horz ? ($h - 1) : 0;
912+                       $sw = $vert ? -$w : $w;
913+                       $sh = $horz ? -$h : $h;
914+
915+                       if ( imagecopyresampled( $dst, $this->image, 0, 0, $sx, $sy, $w, $h, $sw, $sh ) ) {
916+                               imagedestroy( $this->image );
917+                               $this->image = $dst;
918+                               return true;
919+                       }
920+               }
921+
922+               return false; // @TODO: WP_Error here.
923+       }
924+
925+       /**
926+        * Saves current in-memory image to file
927+        *
928+        * @param string $destfilename
929+        * @return array
930+        */
931+       public function save( $destfilename = null ) {
932+               $saved = $this->_save( $this->image, $destfilename );
933+
934+               if ( ! is_wp_error( $saved ) && $destfilename )
935+                       $this->file = $destfilename;
936+
937+               return $saved;
938+       }
939+
940+       protected function _save( $image, $destfilename = null ) {
941+               if ( ! $this->load() )
942+                       return;
943+
944+               if ( null == $destfilename ) {
945+                       $destfilename = $this->generate_filename();
946+               }
947+
948+               if ( 'image/gif' == $this->orig_type ) {
949+                       if ( ! $this->make_image( 'imagegif', $image, $destfilename ) )
950+                               return new WP_Error( 'image_editor_save_failed', __( 'Image Editor Save Failed' ) );
951+               }
952+               elseif ( 'image/png' == $this->orig_type ) {
953+                       // convert from full colors to index colors, like original PNG.
954+                       if ( function_exists('imageistruecolor') && ! imageistruecolor( $image ) )
955+                               imagetruecolortopalette( $image, false, imagecolorstotal( $image ) );
956+
957+                       if ( ! $this->make_image( 'imagepng', $image, $destfilename ) )
958+                               return new WP_Error( 'image_editor_save_failed', __( 'Image Editor Save Failed' ) );
959+               }
960+               else {
961+                       if ( ! $this->make_image( 'imagejpeg', $image, $destfilename, apply_filters( 'jpeg_quality', $this->quality, 'image_resize' ) ) )
962+                               return new WP_Error( 'image_editor_save_failed', __( 'Image Editor Save Failed' ) );
963+               }
964+
965+               // Set correct file permissions
966+               $stat = stat( dirname( $destfilename ) );
967+               $perms = $stat['mode'] & 0000666; //same permissions as parent folder, strip off the executable bits
968+               @ chmod( $destfilename, $perms );
969+
970+               return array(
971+                       'path' => $destfilename,
972+                       'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $destfilename ) ),
973+                       'width' => $this->size['width'],
974+                       'height' => $this->size['height']
975+               );
976+       }
977+
978+       /**
979+        * Returns stream of current image
980+        */
981+       public function stream() {
982+               if ( ! $this->load() )
983+                       return;
984+
985+               switch ( $this->orig_type ) {
986+                       case 'image/jpeg':
987+                               header( 'Content-Type: image/jpeg' );
988+                               return imagejpeg( $this->image, null, $this->quality );
989+                       case 'image/png':
990+                               header( 'Content-Type: image/png' );
991+                               return imagepng( $this->image );
992+                       case 'image/gif':
993+                               header( 'Content-Type: image/gif' );
994+                               return imagegif( $this->image );
995+                       default:
996+                               return false;
997+               }
998+       }
999+
1000+       public function generate_filename( $suffix = null, $dest_path = null ) {
1001+               if ( ! $this->load() )
1002+                       return;
1003+
1004+               return parent::generate_filename( $suffix, $dest_path );
1005+       }
1006+
1007+       public function get_suffix() {
1008+               if ( ! $this->load() )
1009+                       return;
1010+
1011+               return parent::get_suffix();
1012+       }
1013+
1014+       private function make_image( $function, $image, $filename, $quality = -1, $filters = null ) {
1015+               $dst_file = $filename;
1016+
1017+               if ( $stream = wp_is_stream( $filename ) ) {
1018+                       $filename = null;
1019+                       ob_start();
1020+               }
1021+
1022+               $result = call_user_func( $function, $image, $filename, $quality, $filters );
1023+
1024+               if( $result && $stream ) {
1025+                       $contents = ob_get_contents();
1026+
1027+                       $fp = fopen( $dst_file, 'w' );
1028+
1029+                       if( ! $fp )
1030+                               return false;
1031+
1032+                       fwrite( $fp, $contents );
1033+                       fclose( $fp );
1034+               }
1035+
1036+               if( $stream ) {
1037+                       ob_end_clean();
1038+               }
1039+
1040+               return $result;
1041+       }
1042+}
1043\ No newline at end of file
1044diff --git wp-includes/editors/class-wp-image-editor-imagick.php wp-includes/editors/class-wp-image-editor-imagick.php
1045new file mode 100644
1046index 0000000..28f6996
1047--- /dev/null
1048+++ wp-includes/editors/class-wp-image-editor-imagick.php
1049@@ -0,0 +1,308 @@
1050+<?php
1051+
1052+class WP_Image_Editor_Imagick extends WP_Image_Editor_Base {
1053+       private $image = false; // Imagick Object
1054+
1055+       public static function test() {
1056+               if ( ! extension_loaded('imagick') )
1057+                       return false;
1058+
1059+               return true;
1060+       }
1061+
1062+       protected function load() {
1063+               if ( $this->image )
1064+                       return true;
1065+
1066+               if ( ! file_exists( $this->file ) )
1067+                       return sprintf( __('File &#8220;%s&#8221; doesn&#8217;t exist?'), $this->file );
1068+
1069+               try {
1070+                       $this->image = new Imagick( $this->file );
1071+               }
1072+               catch ( Exception $e ) {
1073+                       return sprintf(__('File &#8220;%s&#8221; is not an image.'), $this->file);
1074+               }
1075+
1076+               if( ! $this->image->valid() ) {
1077+                       return sprintf(__('File &#8220;%s&#8221; is not an image.'), $this->file);
1078+               }
1079+
1080+               $this->update_size();
1081+               $this->orig_type  = $this->image->getImageFormat(); // TODO: Wrap in exception handling
1082+               if ( ! $this->size )
1083+                       return new WP_Error( 'invalid_image', __('Could not read image size'), $this->file );
1084+
1085+               return true;
1086+       }
1087+
1088+       public function get_size() {
1089+               if ( ! $this->load() )
1090+                       return;
1091+
1092+               return parent::get_size();
1093+       }
1094+
1095+       protected function update_size( $width = false, $height = false ) {
1096+               if ( ! $this->load() )
1097+                       return false;
1098+
1099+               $size = null;
1100+               if ( !$width || !$height ) {
1101+                       try {
1102+                               $size = $this->image->getImageGeometry();
1103+                       }
1104+                       catch ( Exception $e ) {
1105+                               return sprintf(__('File &#8220;%s&#8221; couldn\'t be checked for size.'), $this->file);
1106+                       }
1107+               }
1108+
1109+               parent::update_size( $width ?: $size['width'], $height ?: $size['height'] );
1110+       }
1111+
1112+       public function resize( $max_w, $max_h, $crop = false ) {
1113+               if ( ! $this->load() )
1114+                       return false;
1115+
1116+               $dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop );
1117+               if ( ! $dims )
1118+                       return new WP_Error( 'error_getting_dimensions', __('Could not calculate resized image dimensions') );
1119+               list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims;
1120+
1121+               if( 'JPEG' == $this->orig_type ) {
1122+                       $this->image->setImageCompressionQuality( apply_filters( 'jpeg_quality', $this->quality, 'image_resize' ) );
1123+                       $this->image->setImageCompression( imagick::COMPRESSION_JPEG );
1124+               }
1125+               else {
1126+                       $this->image->setImageCompressionQuality( $this->quality );
1127+               }
1128+
1129+               if ( $crop ) {
1130+                       return $this->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h );
1131+               }
1132+
1133+               //$this->image->thumbnailImage( $dst_w, $dst_h );
1134+               $this->image->scaleImage( $dst_w, $dst_h );
1135+               $this->update_size( $dst_w, $dst_h );
1136+
1137+               return $this->image;
1138+       }
1139+
1140+       /**
1141+        * Processes current image and saves to disk
1142+        * multiple sizes from single source.
1143+        *
1144+        * @param array $sizes
1145+        * @return array
1146+        */
1147+       public function multi_resize( $sizes ) {
1148+               $metadata = array();
1149+               if ( ! $this->load() )
1150+                       return $metadata;
1151+
1152+               $orig_size = $this->size;
1153+               $orig_image = $this->image->getImage();
1154+               foreach ( $sizes as $size => $size_data ) {
1155+                       if ( ! $this->image )
1156+                               $this->image = $orig_image->getImage();
1157+
1158+                       $image = $this->resize( $size_data['width'], $size_data['height'], $size_data['crop'] );
1159+
1160+                       if( ! is_wp_error( $image ) ) {
1161+                               $resized = $this->_save( $image );
1162+
1163+                               $this->image->destroy();
1164+                               $this->image = null;
1165+                               unset( $resized['path'] );
1166+
1167+                               if ( ! is_wp_error( $resized ) && $resized )
1168+                                       $metadata[$size] = $resized;
1169+                       }
1170+
1171+                       $this->size = $orig_size;
1172+               }
1173+
1174+               $this->image = $orig_image;
1175+
1176+               return $metadata;
1177+       }
1178+
1179+       /**
1180+        * Crops image.
1181+        *
1182+        * @param float $x
1183+        * @param float $y
1184+        * @param float $w
1185+        * @param float $h
1186+        * @return boolean
1187+        */
1188+       public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) {
1189+               if ( ! $this->load() )
1190+                       return;
1191+
1192+               // Not sure this is compatible.
1193+               if ( $src_abs ) {
1194+                       $src_w -= $src_x;
1195+                       $src_h -= $src_y;
1196+               }
1197+
1198+               if( 'JPEG' == $this->orig_type ) {
1199+                       $this->image->setImageCompressionQuality( apply_filters( 'jpeg_quality', $this->quality, 'image_resize' ) );
1200+                       $this->image->setImageCompression( imagick::COMPRESSION_JPEG );
1201+               }
1202+               else {
1203+                       $this->image->setImageCompressionQuality( $this->quality );
1204+               }
1205+
1206+               $this->image->cropImage( $src_w, $src_h, $src_x, $src_y );
1207+
1208+               if ( $dst_w || $dst_h ) {
1209+                       // If destination width/height isn't specified, use same as
1210+                       // width/height from source.
1211+                       $dst_w = $dst_w ?: $src_w;
1212+                       $dst_h = $dst_h ?: $src_h;
1213+
1214+                       $this->image->scaleImage( $dst_w, $dst_h );
1215+                       $this->update_size( $dst_w, $dst_h );
1216+                       return true;
1217+               }
1218+
1219+               $this->update_size( $src_w, $src_h );
1220+               return true;
1221+
1222+               // @TODO: We need exception handling above  // return false;
1223+       }
1224+
1225+       /**
1226+        * Rotates in memory image by $angle.
1227+        * Ported from image-edit.php
1228+        *
1229+        * @param float $angle
1230+        * @return boolean
1231+        */
1232+       public function rotate( $angle ) {
1233+               if ( ! $this->load() )
1234+                       return;
1235+
1236+               /**
1237+                * $angle is 360-$angle because Imagick rotates clockwise
1238+                * (GD rotates counter-clockwise)
1239+                */
1240+               try {
1241+                       $this->image->rotateImage( new ImagickPixel('none'), 360-$angle );
1242+                       $this->update_size();
1243+               }
1244+               catch ( Exception $e ) {
1245+                       return false; // TODO: WP_Error Here.
1246+               }
1247+       }
1248+
1249+       /**
1250+        * Flips Image
1251+        *
1252+        * @param boolean $horz
1253+        * @param boolean $vert
1254+        * @returns boolean
1255+        */
1256+       public function flip( $horz, $vert ) {
1257+               if ( ! $this->load() )
1258+                       return;
1259+
1260+               try {
1261+                       if ( $horz )
1262+                               $this->image->flipImage();
1263+
1264+                       if ( $vert )
1265+                               $this->image->flopImage();
1266+               }
1267+               catch ( Exception $e ) {
1268+                       return false; // TODO: WP_Error Here.
1269+               }
1270+
1271+               return true;
1272+       }
1273+
1274+       /**
1275+        * Saves current image to file
1276+        *
1277+        * @param string $destfilename
1278+        * @return array
1279+        */
1280+       public function save( $destfilename = null ) {
1281+               $saved = $this->_save( $this->image, $destfilename );
1282+
1283+               if ( ! is_wp_error( $saved ) && $destfilename )
1284+                       $this->file = $destfilename;
1285+
1286+               return $saved;
1287+       }
1288+
1289+       protected function _save( $image, $destfilename = null ) {
1290+               if ( ! $this->load() )
1291+                       return;
1292+
1293+               if ( null == $destfilename ) {
1294+                       $destfilename = $this->generate_filename();
1295+               }
1296+
1297+               if( apply_filters( 'wp_editors_stripimage', true ) ) {
1298+                       $this->image->stripImage();
1299+               }
1300+
1301+               $this->image->writeImage( $destfilename );
1302+
1303+               // Set correct file permissions
1304+               $stat = stat( dirname( $destfilename ) );
1305+               $perms = $stat['mode'] & 0000666; //same permissions as parent folder, strip off the executable bits
1306+               @ chmod( $destfilename, $perms );
1307+
1308+               return array(
1309+                       'path' => $destfilename,
1310+                       'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $destfilename ) ),
1311+                       'width' => $this->size['width'],
1312+                       'height' => $this->size['height']
1313+               );
1314+       }
1315+
1316+       /**
1317+        * @TODO: Wrap in try and clean up.
1318+        * Also, make GIF not stream the last frame :(
1319+        *
1320+        * @return boolean
1321+        */
1322+       public function stream() {
1323+               if ( ! $this->load() )
1324+                       return;
1325+
1326+               switch ( $this->orig_type ) {
1327+                       case 'JPEG':
1328+                               header( 'Content-Type: image/jpeg' );
1329+                               break;
1330+                       case 'PNG':
1331+                               header( 'Content-Type: image/png' );
1332+                               break;
1333+                       case 'GIF':
1334+                               header( 'Content-Type: image/gif' );
1335+                               break;
1336+                       default:
1337+                               return false;
1338+               }
1339+
1340+               print $this->image->getImageBlob();
1341+               return true;
1342+       }
1343+
1344+       public function generate_filename( $suffix = null, $dest_path = null ) {
1345+               if ( ! $this->load() )
1346+                       return;
1347+
1348+               return parent::generate_filename( $suffix, $dest_path );
1349+       }
1350+
1351+       public function get_suffix() {
1352+               if ( ! $this->load() )
1353+                       return;
1354+
1355+               return parent::get_suffix();
1356+       }
1357+}
1358\ No newline at end of file
1359diff --git wp-includes/functions.php wp-includes/functions.php
1360index 91314b5..016062f 100644
1361--- wp-includes/functions.php
1362+++ wp-includes/functions.php
1363@@ -1292,9 +1292,21 @@ function wp_get_original_referer() {
1364  * @return bool Whether the path was created. True if path already exists.
1365  */
1366 function wp_mkdir_p( $target ) {
1367+       $wrapper = null;
1368+
1369+       // strip the protocol
1370+       if( wp_is_stream( $target ) ) {
1371+               list( $wrapper, $target ) = explode( '://', $target, 2 );
1372+       }
1373+
1374        // from php.net/mkdir user contributed notes
1375        $target = str_replace( '//', '/', $target );
1376 
1377+       // put the wrapper back on the target
1378+       if( $wrapper !== null ) {
1379+               $target = $wrapper . '://' . $target;
1380+       }
1381+
1382        // safe mode fails with a trailing slash under certain PHP versions.
1383        $target = rtrim($target, '/'); // Use rtrim() instead of untrailingslashit to avoid formatting.php dependency.
1384        if ( empty($target) )
1385@@ -3684,3 +3696,15 @@ function _device_can_upload() {
1386        return true;
1387 }
1388 
1389+/**
1390+ * Test if a given path is a stream URL
1391+ *
1392+ * @param string $path The resource path or URL
1393+ * @return bool True if the path is a stream URL
1394+ */
1395+function wp_is_stream( $path ) {
1396+       $wrappers = stream_get_wrappers();
1397+       $wrappers_re = '(' . join('|', $wrappers) . ')';
1398+
1399+       return preg_match( "!^$wrappers_re://!", $path ) === 1;
1400+}
1401diff --git wp-includes/media.php wp-includes/media.php
1402index 8839fd9..c311cb9 100644
1403--- wp-includes/media.php
1404+++ wp-includes/media.php
1405@@ -236,34 +236,6 @@ function get_image_tag($id, $alt, $title, $align, $size='medium') {
1406 }
1407 
1408 /**
1409- * Load an image from a string, if PHP supports it.
1410- *
1411- * @since 2.1.0
1412- *
1413- * @param string $file Filename of the image to load.
1414- * @return resource The resulting image resource on success, Error string on failure.
1415- */
1416-function wp_load_image( $file ) {
1417-       if ( is_numeric( $file ) )
1418-               $file = get_attached_file( $file );
1419-
1420-       if ( ! file_exists( $file ) )
1421-               return sprintf(__('File &#8220;%s&#8221; doesn&#8217;t exist?'), $file);
1422-
1423-       if ( ! function_exists('imagecreatefromstring') )
1424-               return __('The GD image library is not installed.');
1425-
1426-       // Set artificially high because GD uses uncompressed images in memory
1427-       @ini_set( 'memory_limit', apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT ) );
1428-       $image = imagecreatefromstring( file_get_contents( $file ) );
1429-
1430-       if ( !is_resource( $image ) )
1431-               return sprintf(__('File &#8220;%s&#8221; is not an image.'), $file);
1432-
1433-       return $image;
1434-}
1435-
1436-/**
1437  * Calculates the new dimensions for a downsampled image.
1438  *
1439  * If either width or height are empty, no constraint is applied on
1440@@ -393,92 +365,6 @@ function image_resize_dimensions($orig_w, $orig_h, $dest_w, $dest_h, $crop = fal
1441 }
1442 
1443 /**
1444- * Scale down an image to fit a particular size and save a new copy of the image.
1445- *
1446- * The PNG transparency will be preserved using the function, as well as the
1447- * image type. If the file going in is PNG, then the resized image is going to
1448- * be PNG. The only supported image types are PNG, GIF, and JPEG.
1449- *
1450- * Some functionality requires API to exist, so some PHP version may lose out
1451- * support. This is not the fault of WordPress (where functionality is
1452- * downgraded, not actual defects), but of your PHP version.
1453- *
1454- * @since 2.5.0
1455- *
1456- * @param string $file Image file path.
1457- * @param int $max_w Maximum width to resize to.
1458- * @param int $max_h Maximum height to resize to.
1459- * @param bool $crop Optional. Whether to crop image or resize.
1460- * @param string $suffix Optional. File suffix.
1461- * @param string $dest_path Optional. New image file path.
1462- * @param int $jpeg_quality Optional, default is 90. Image quality percentage.
1463- * @return mixed WP_Error on failure. String with new destination path.
1464- */
1465-function image_resize( $file, $max_w, $max_h, $crop = false, $suffix = null, $dest_path = null, $jpeg_quality = 90 ) {
1466-
1467-       $image = wp_load_image( $file );
1468-       if ( !is_resource( $image ) )
1469-               return new WP_Error( 'error_loading_image', $image, $file );
1470-
1471-       $size = @getimagesize( $file );
1472-       if ( !$size )
1473-               return new WP_Error('invalid_image', __('Could not read image size'), $file);
1474-       list($orig_w, $orig_h, $orig_type) = $size;
1475-
1476-       $dims = image_resize_dimensions($orig_w, $orig_h, $max_w, $max_h, $crop);
1477-       if ( !$dims )
1478-               return new WP_Error( 'error_getting_dimensions', __('Could not calculate resized image dimensions') );
1479-       list($dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) = $dims;
1480-
1481-       $newimage = wp_imagecreatetruecolor( $dst_w, $dst_h );
1482-
1483-       imagecopyresampled( $newimage, $image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
1484-
1485-       // convert from full colors to index colors, like original PNG.
1486-       if ( IMAGETYPE_PNG == $orig_type && function_exists('imageistruecolor') && !imageistruecolor( $image ) )
1487-               imagetruecolortopalette( $newimage, false, imagecolorstotal( $image ) );
1488-
1489-       // we don't need the original in memory anymore
1490-       imagedestroy( $image );
1491-
1492-       // $suffix will be appended to the destination filename, just before the extension
1493-       if ( !$suffix )
1494-               $suffix = "{$dst_w}x{$dst_h}";
1495-
1496-       $info = pathinfo($file);
1497-       $dir = $info['dirname'];
1498-       $ext = $info['extension'];
1499-       $name = wp_basename($file, ".$ext");
1500-
1501-       if ( !is_null($dest_path) and $_dest_path = realpath($dest_path) )
1502-               $dir = $_dest_path;
1503-       $destfilename = "{$dir}/{$name}-{$suffix}.{$ext}";
1504-
1505-       if ( IMAGETYPE_GIF == $orig_type ) {
1506-               if ( !imagegif( $newimage, $destfilename ) )
1507-                       return new WP_Error('resize_path_invalid', __( 'Resize path invalid' ));
1508-       } elseif ( IMAGETYPE_PNG == $orig_type ) {
1509-               if ( !imagepng( $newimage, $destfilename ) )
1510-                       return new WP_Error('resize_path_invalid', __( 'Resize path invalid' ));
1511-       } else {
1512-               // all other formats are converted to jpg
1513-               if ( 'jpg' != $ext && 'jpeg' != $ext )
1514-                       $destfilename = "{$dir}/{$name}-{$suffix}.jpg";
1515-               if ( !imagejpeg( $newimage, $destfilename, apply_filters( 'jpeg_quality', $jpeg_quality, 'image_resize' ) ) )
1516-                       return new WP_Error('resize_path_invalid', __( 'Resize path invalid' ));
1517-       }
1518-
1519-       imagedestroy( $newimage );
1520-
1521-       // Set correct file permissions
1522-       $stat = stat( dirname( $destfilename ));
1523-       $perms = $stat['mode'] & 0000666; //same permissions as parent folder, strip off the executable bits
1524-       @ chmod( $destfilename, $perms );
1525-
1526-       return $destfilename;
1527-}
1528-
1529-/**
1530  * Resize an image to make a thumbnail or intermediate size.
1531  *
1532  * The returned array has the file size, the image width, and image height. The
1533@@ -493,16 +379,17 @@ function image_resize( $file, $max_w, $max_h, $crop = false, $suffix = null, $de
1534  * @param bool $crop Optional, default is false. Whether to crop image to specified height and width or resize.
1535  * @return bool|array False, if no image was created. Metadata array on success.
1536  */
1537-function image_make_intermediate_size($file, $width, $height, $crop=false) {
1538+function image_make_intermediate_size( $file, $width, $height, $crop = false ) {
1539        if ( $width || $height ) {
1540-               $resized_file = image_resize($file, $width, $height, $crop);
1541-               if ( !is_wp_error($resized_file) && $resized_file && $info = getimagesize($resized_file) ) {
1542-                       $resized_file = apply_filters('image_make_intermediate_size', $resized_file);
1543-                       return array(
1544-                               'file' => wp_basename( $resized_file ),
1545-                               'width' => $info[0],
1546-                               'height' => $info[1],
1547-                       );
1548+               $editor = WP_Image_Editor::get_instance( $file );
1549+               $editor->resize( $width, $height, $crop );
1550+               $resized_file = $editor->save();
1551+
1552+               unset( $editor );
1553+
1554+               if ( ! is_wp_error( $resized_file ) && $resized_file ) {
1555+                       unset( $resized_file['path'] );
1556+                       return $resized_file;
1557                }
1558        }
1559        return false;
1560diff --git wp-settings.php wp-settings.php
1561index 2db338b..80fd5d5 100644
1562--- wp-settings.php
1563+++ wp-settings.php
1564@@ -142,6 +142,11 @@ require( ABSPATH . WPINC . '/nav-menu.php' );
1565 require( ABSPATH . WPINC . '/nav-menu-template.php' );
1566 require( ABSPATH . WPINC . '/admin-bar.php' );
1567 
1568+require( ABSPATH . WPINC . '/class-wp-image-editor.php' );
1569+require( ABSPATH . WPINC . '/editors/class-wp-image-editor-base.php' );
1570+require( ABSPATH . WPINC . '/editors/class-wp-image-editor-gd.php' );
1571+require( ABSPATH . WPINC . '/editors/class-wp-image-editor-imagick.php' );
1572+
1573 // Load multisite-specific files.
1574 if ( is_multisite() ) {
1575        require( ABSPATH . WPINC . '/ms-functions.php' );