WordPress.org

Make WordPress Core

Ticket #6821: 6821.9.3.diff

File 6821.9.3.diff, 57.4 KB (added by mikeschroder, 7 years ago)

Refresh, and more docs.

  • wp-admin/includes/image-edit.php

    diff --git wp-admin/includes/image-edit.php wp-admin/includes/image-edit.php
    index 8185c20..75b6834 100644
    function wp_image_editor($post_id, $msg = false) { 
    197197<?php
    198198}
    199199
    200 function wp_stream_image($image, $mime_type, $post_id) {
    201         $image = apply_filters('image_save_pre', $image, $post_id);
    202 
    203         switch ( $mime_type ) {
    204                 case 'image/jpeg':
    205                         header('Content-Type: image/jpeg');
    206                         return imagejpeg($image, null, 90);
    207                 case 'image/png':
    208                         header('Content-Type: image/png');
    209                         return imagepng($image);
    210                 case 'image/gif':
    211                         header('Content-Type: image/gif');
    212                         return imagegif($image);
    213                 default:
     200/**
     201 * Streams image in WP_Image_Editor to browser.
     202 * Provided for backcompat reasons
     203 *
     204 * @param WP_Image_Editor $image
     205 * @param string $mime_type
     206 * @param int $post_id
     207 * @return boolean
     208 */
     209function wp_stream_image( $image, $mime_type, $post_id ) {
     210        if ( $image instanceof WP_Image_Editor ) {
     211                $image = apply_filters('image_editor_save_pre', $image, $post_id);
     212
     213                if ( is_wp_error( $image->stream( $mime_type ) ) )
    214214                        return false;
     215
     216                return true;
     217
     218    } else {
     219                _deprecated_argument( __FUNCTION__, '3.5', __( '$image needs to be an WP_Image_Editor object' ) );
     220
     221                $image = apply_filters('image_save_pre', $image, $post_id);
     222
     223                switch ( $mime_type ) {
     224                        case 'image/jpeg':
     225                                header( 'Content-Type: image/jpeg' );
     226                                return imagejpeg( $image, null, 90 );
     227                        case 'image/png':
     228                                header( 'Content-Type: image/png' );
     229                                return imagepng( $image );
     230                        case 'image/gif':
     231                                header( 'Content-Type: image/gif' );
     232                                return imagegif( $image );
     233                        default:
     234                                return false;
     235                }
    215236        }
    216237}
    217238
    218 function wp_save_image_file($filename, $image, $mime_type, $post_id) {
    219         $image = apply_filters('image_save_pre', $image, $post_id);
    220         $saved = apply_filters('wp_save_image_file', null, $filename, $image, $mime_type, $post_id);
    221         if ( null !== $saved )
    222                 return $saved;
    223 
    224         switch ( $mime_type ) {
    225                 case 'image/jpeg':
    226                         return imagejpeg( $image, $filename, apply_filters( 'jpeg_quality', 90, 'edit_image' ) );
    227                 case 'image/png':
    228                         return imagepng($image, $filename);
    229                 case 'image/gif':
    230                         return imagegif($image, $filename);
    231                 default:
    232                         return false;
     239/**
     240 * Saves Image to File
     241 * @TODO: Add mime_type support to WP_Image_Editor
     242 *
     243 * @param string $filename
     244 * @param WP_Image_Editor $image
     245 * @param string $mime_type
     246 * @param int $post_id
     247 * @return boolean
     248 */
     249function wp_save_image_file( $filename, $image, $mime_type, $post_id ) {
     250        if ( $image instanceof WP_Image_Editor ) {
     251                $image = apply_filters('image_editor_save_pre', $image, $post_id);
     252                $saved = apply_filters('wp_save_image_editor_file', null, $filename, $image, $mime_type, $post_id);
     253
     254                if ( null !== $saved )
     255                        return $saved;
     256
     257                return $image->save( $filename, $mime_type );
     258        } else {
     259                _deprecated_argument( __FUNCTION__, '3.5', __( '$image needs to be an WP_Image_Editor object' ) );
     260
     261                $image = apply_filters('image_save_pre', $image, $post_id);
     262                $saved = apply_filters('wp_save_image_file', null, $filename, $image, $mime_type, $post_id);
     263
     264                if ( null !== $saved )
     265                        return $saved;
     266
     267                switch ( $mime_type ) {
     268                        case 'image/jpeg':
     269                                return imagejpeg( $image, $filename, apply_filters( 'jpeg_quality', 90, 'edit_image' ) );
     270                        case 'image/png':
     271                                return imagepng( $image, $filename );
     272                        case 'image/gif':
     273                                return imagegif( $image, $filename );
     274                        default:
     275                                return false;
     276                }
    233277        }
    234278}
    235279
    function _image_get_preview_ratio($w, $h) { 
    238282        return $max > 400 ? (400 / $max) : 1;
    239283}
    240284
     285// @TODO: Returns GD resource, but is NOT public
    241286function _rotate_image_resource($img, $angle) {
     287        _deprecated_function( __FUNCTION__, '3.5', __( 'Use WP_Image_Editor::rotate' ) );
    242288        if ( function_exists('imagerotate') ) {
    243289                $rotated = imagerotate($img, $angle, 0);
    244290                if ( is_resource($rotated) ) {
    function _rotate_image_resource($img, $angle) { 
    249295        return $img;
    250296}
    251297
     298/**
     299 * @TODO: Only used within image_edit_apply_changes
     300 *                and receives/returns GD Resource.
     301 *                Consider removal.
     302 *
     303 * @param GD_Resource $img
     304 * @param boolean $horz
     305 * @param boolean $vert
     306 * @return GD_Resource
     307 */
    252308function _flip_image_resource($img, $horz, $vert) {
     309        _deprecated_function( __FUNCTION__, '3.5', __( 'Use WP_Image_Editor::flip' ) );
    253310        $w = imagesx($img);
    254311        $h = imagesy($img);
    255312        $dst = wp_imagecreatetruecolor($w, $h);
    function _flip_image_resource($img, $horz, $vert) { 
    267324        return $img;
    268325}
    269326
     327/**
     328 * @TODO: Only used within image_edit_apply_changes
     329 *                and receives/returns GD Resource.
     330 *                Consider removal.
     331 *
     332 * @param GD_Resource $img
     333 * @param float $x
     334 * @param float $y
     335 * @param float $w
     336 * @param float $h
     337 * @return GD_Resource
     338 */
    270339function _crop_image_resource($img, $x, $y, $w, $h) {
    271340        $dst = wp_imagecreatetruecolor($w, $h);
    272341        if ( is_resource($dst) ) {
    function _crop_image_resource($img, $x, $y, $w, $h) { 
    278347        return $img;
    279348}
    280349
    281 function image_edit_apply_changes($img, $changes) {
     350/**
     351 * Performs group of changes on Editor specified.
     352 *
     353 * @param WP_Image_Editor $image
     354 * @param type $changes
     355 * @return WP_Image_Editor
     356 */
     357function image_edit_apply_changes( $image, $changes ) {
     358        if ( is_resource( $image ) )
     359                _deprecated_argument( __FUNCTION__, '3.5', __( '$image needs to be an WP_Image_Editor object' ) );
    282360
    283361        if ( !is_array($changes) )
    284                 return $img;
     362                return $image;
    285363
    286364        // expand change operations
    287365        foreach ( $changes as $key => $obj ) {
    function image_edit_apply_changes($img, $changes) { 
    326404        }
    327405
    328406        // image resource before applying the changes
    329         $img = apply_filters('image_edit_before_change', $img, $changes);
     407        if ( $image instanceof WP_Image_Editor )
     408                $image = apply_filters('wp_image_editor_before_change', $image, $changes);
     409        elseif ( is_resource( $image ) )
     410                $image = apply_filters('image_edit_before_change', $image, $changes);
    330411
    331412        foreach ( $changes as $operation ) {
    332413                switch ( $operation->type ) {
    333414                        case 'rotate':
    334                                 if ( $operation->angle != 0 )
    335                                         $img = _rotate_image_resource($img, $operation->angle);
     415                                if ( $operation->angle != 0 ) {
     416                                        if ( $image instanceof WP_Image_Editor )
     417                                                $image->rotate( $operation->angle );
     418                                        else
     419                                                $image = _rotate_image_resource( $image, $operation->angle );
     420                                }
    336421                                break;
    337422                        case 'flip':
    338423                                if ( $operation->axis != 0 )
    339                                         $img = _flip_image_resource($img, ($operation->axis & 1) != 0, ($operation->axis & 2) != 0);
     424                                        if ( $image instanceof WP_Image_Editor )
     425                                                $image->flip( ($operation->axis & 1) != 0, ($operation->axis & 2) != 0 );
     426                                        else
     427                                                $image = _flip_image_resource( $image, ( $operation->axis & 1 ) != 0, ( $operation->axis & 2 ) != 0 );
    340428                                break;
    341429                        case 'crop':
    342430                                $sel = $operation->sel;
    343                                 $scale = 1 / _image_get_preview_ratio( imagesx($img), imagesy($img) ); // discard preview scaling
    344                                 $img = _crop_image_resource($img, $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale);
     431
     432                                if ( $image instanceof WP_Image_Editor ) {
     433                                        $size = $image->get_size();
     434                                        $w = $size['width'];
     435                                        $h = $size['height'];
     436
     437                                        $scale = 1 / _image_get_preview_ratio( $w, $h ); // discard preview scaling
     438                                        $image->crop( $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale );
     439                                } else {
     440                                        $scale = 1 / _image_get_preview_ratio( imagesx( $image ), imagesy( $image ) ); // discard preview scaling
     441                                        $image = _crop_image_resource( $image, $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale );
     442                                }
    345443                                break;
    346444                }
    347445        }
    348446
    349         return $img;
     447        return $image;
    350448}
    351449
    352 function stream_preview_image($post_id) {
    353         $post = get_post($post_id);
     450
     451/**
     452 * Streams image in post to browser, along with enqueued changes
     453 * in $_REQUEST['history']
     454 *
     455 * @param int $post_id
     456 * @return boolean
     457 */
     458function stream_preview_image( $post_id ) {
     459        $post = get_post( $post_id );
    354460        @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
    355         $img = load_image_to_edit( $post_id, $post->post_mime_type, array(400, 400) );
    356461
    357         if ( !is_resource($img) )
    358                 return false;
     462        $img = WP_Image_Editor::get_instance( _load_image_to_edit_path( $post_id ) );
     463
     464    if ( is_wp_error( $img ) )
     465        return false;
    359466
    360467        $changes = !empty($_REQUEST['history']) ? json_decode( stripslashes($_REQUEST['history']) ) : null;
    361468        if ( $changes )
    362                 $img = image_edit_apply_changes($img, $changes);
     469                $img = image_edit_apply_changes( $img, $changes );
    363470
    364471        // scale the image
    365         $w = imagesx($img);
    366         $h = imagesy($img);
    367         $ratio = _image_get_preview_ratio($w, $h);
     472        $size = $img->get_size();
     473        $w = $size['width'];
     474        $h = $size['height'];
     475
     476        $ratio = _image_get_preview_ratio( $w, $h );
    368477        $w2 = $w * $ratio;
    369478        $h2 = $h * $ratio;
    370479
    371         $preview = wp_imagecreatetruecolor($w2, $h2);
    372         imagecopyresampled( $preview, $img, 0, 0, 0, 0, $w2, $h2, $w, $h );
    373         wp_stream_image($preview, $post->post_mime_type, $post_id);
     480        if ( is_wp_error( $img->resize( $w2, $h2 ) ) )
     481                return false;
    374482
    375         imagedestroy($preview);
    376         imagedestroy($img);
    377         return true;
     483        return wp_stream_image( $img, $post->post_mime_type, $post_id );
    378484}
    379485
    380486function wp_restore_image($post_id) {
    function wp_restore_image($post_id) { 
    450556        return $msg;
    451557}
    452558
    453 function wp_save_image($post_id) {
     559/**
     560 * Saves image to post along with enqueued changes
     561 * in $_REQUEST['history']
     562 *
     563 * @param int $post_id
     564 * @return \stdClass
     565 */
     566function wp_save_image( $post_id ) {
    454567        $return = new stdClass;
    455568        $success = $delete = $scaled = $nocrop = false;
    456         $post = get_post($post_id);
    457         @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
    458         $img = load_image_to_edit($post_id, $post->post_mime_type);
     569        $post = get_post( $post_id );
    459570
    460         if ( !is_resource($img) ) {
     571        $img = WP_Image_Editor::get_instance( _load_image_to_edit_path( $post_id, 'full' ) );
     572        if ( !$img ) {
    461573                $return->error = esc_js( __('Unable to create new image.') );
    462574                return $return;
    463575        }
    function wp_save_image($post_id) { 
    468580        $scale = !empty($_REQUEST['do']) && 'scale' == $_REQUEST['do'];
    469581
    470582        if ( $scale && $fwidth > 0 && $fheight > 0 ) {
    471                 $sX = imagesx($img);
    472                 $sY = imagesy($img);
     583                $size = $img->get_size();
     584                $sX = $size['width'];
     585                $sY = $size['height'];
    473586
    474587                // check if it has roughly the same w / h ratio
    475588                $diff = round($sX / $sY, 2) - round($fwidth / $fheight, 2);
    476589                if ( -0.1 < $diff && $diff < 0.1 ) {
    477590                        // scale the full size image
    478                         $dst = wp_imagecreatetruecolor($fwidth, $fheight);
    479                         if ( imagecopyresampled( $dst, $img, 0, 0, 0, 0, $fwidth, $fheight, $sX, $sY ) ) {
    480                                 imagedestroy($img);
    481                                 $img = $dst;
     591                        if ( $img->resize( $fwidth, $fheight ) )
    482592                                $scaled = true;
    483                         }
    484593                }
    485594
    486595                if ( !$scaled ) {
    function wp_save_image($post_id) { 
    551660                if ( $tag )
    552661                        $backup_sizes[$tag] = array('width' => $meta['width'], 'height' => $meta['height'], 'file' => $path_parts['basename']);
    553662
    554                 $success = update_attached_file($post_id, $new_path);
     663                $success = update_attached_file( $post_id, $new_path );
     664
     665                $meta['file'] = _wp_relative_upload_path( $new_path );
    555666
    556                 $meta['file'] = _wp_relative_upload_path($new_path);
    557                 $meta['width'] = imagesx($img);
    558                 $meta['height'] = imagesy($img);
     667                $size = $img->get_size();
     668                $meta['width'] = $size['width'];
     669                $meta['height'] = $size['height'];
    559670
    560671                if ( $success && ('nothumb' == $target || 'all' == $target) ) {
    561672                        $sizes = get_intermediate_image_sizes();
    function wp_save_image($post_id) { 
    570681                $success = $delete = $nocrop = true;
    571682        }
    572683
    573         if ( isset($sizes) ) {
     684        if ( isset( $sizes ) ) {
     685                $_sizes = array();
     686
    574687                foreach ( $sizes as $size ) {
    575688                        $tag = false;
    576                         if ( isset($meta['sizes'][$size]) ) {
     689                        if ( isset( $meta['sizes'][$size] ) ) {
    577690                                if ( isset($backup_sizes["$size-orig"]) ) {
    578691                                        if ( ( !defined('IMAGE_EDIT_OVERWRITE') || !IMAGE_EDIT_OVERWRITE ) && $backup_sizes["$size-orig"]['file'] != $meta['sizes'][$size]['file'] )
    579692                                                $tag = "$size-$suffix";
    function wp_save_image($post_id) { 
    586699                        }
    587700
    588701                        $crop = $nocrop ? false : get_option("{$size}_crop");
    589                         $resized = image_make_intermediate_size($new_path, get_option("{$size}_size_w"), get_option("{$size}_size_h"), $crop );
    590 
    591                         if ( $resized )
    592                                 $meta['sizes'][$size] = $resized;
    593                         else
    594                                 unset($meta['sizes'][$size]);
     702                        $_sizes[ $size ] = array( 'width' => get_option("{$size}_size_w"), 'height' => get_option("{$size}_size_h"), 'crop' => $crop );
    595703                }
     704
     705                $meta['sizes'] = $img->multi_resize( $_sizes );
    596706        }
    597707
     708        unset( $img );
     709
    598710        if ( $success ) {
    599                 wp_update_attachment_metadata($post_id, $meta);
     711                wp_update_attachment_metadata( $post_id, $meta );
    600712                update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes);
    601713
    602714                if ( $target == 'thumbnail' || $target == 'all' || $target == 'full' ) {
    function wp_save_image($post_id) { 
    612724
    613725        if ( $delete ) {
    614726                $delpath = apply_filters('wp_delete_file', $new_path);
    615                 @unlink($delpath);
     727                @unlink( $delpath );
    616728        }
    617729
    618         imagedestroy($img);
    619 
    620730        $return->msg = esc_js( __('Image saved') );
    621731        return $return;
    622732}
  • wp-admin/includes/image.php

    diff --git wp-admin/includes/image.php wp-admin/includes/image.php
    index c7638cd..2a5123e 100644
     
    2222 * @param string $dst_file Optional. The destination file to write to.
    2323 * @return string|WP_Error|false New filepath on success, WP_Error or false on failure.
    2424 */
    25 function wp_crop_image( $src, $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs = false, $dst_file = false ) {
    26         if ( is_numeric( $src ) ) { // Handle int as attachment ID
    27                 $src_file = get_attached_file( $src );
     25function wp_crop_image( $src_file, $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs = false, $dst_file = false ) {
     26        if ( is_numeric( $src_file ) ) { // Handle int as attachment ID
     27                $src_file = get_attached_file( $src_file );
    2828                if ( ! file_exists( $src_file ) ) {
    2929                        // If the file doesn't exist, attempt a url fopen on the src link.
    3030                        // This can occur with certain file replication plugins.
    31                         $post = get_post( $src );
    32                         $image_type = $post->post_mime_type;
    33                         $src = load_image_to_edit( $src, $post->post_mime_type, 'full' );
    34                 } else {
    35                         $size = @getimagesize( $src_file );
    36                         $image_type = ( $size ) ? $size['mime'] : '';
    37                         $src = wp_load_image( $src_file );
     31                        $src_file = _load_image_to_edit_path( $src_file, 'full' );
    3832                }
    39         } else {
    40                 $size = @getimagesize( $src );
    41                 $image_type = ( $size ) ? $size['mime'] : '';
    42                 $src = wp_load_image( $src );
    43         }
    44 
    45         if ( ! is_resource( $src ) )
    46                 return new WP_Error( 'error_loading_image', $src, $src_file );
    47 
    48         $dst = wp_imagecreatetruecolor( $dst_w, $dst_h );
    49 
    50         if ( $src_abs ) {
    51                 $src_w -= $src_x;
    52                 $src_h -= $src_y;
    5333        }
    5434
    55         if ( function_exists( 'imageantialias' ) )
    56                 imageantialias( $dst, true );
    57 
    58         imagecopyresampled( $dst, $src, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h );
     35        $editor = WP_Image_Editor::get_instance( $src_file );
     36        $src = $editor->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs );
    5937
    60         imagedestroy( $src ); // Free up memory
     38        if ( is_wp_error( $src ) )
     39                return $src;
    6140
    6241        if ( ! $dst_file )
    6342                $dst_file = str_replace( basename( $src_file ), 'cropped-' . basename( $src_file ), $src_file );
    6443
    65         if ( 'image/png' != $image_type )
    66                 $dst_file = preg_replace( '/\\.[^\\.]+$/', '.jpg', $dst_file );
    67 
    6844        // The directory containing the original file may no longer exist when
    6945        // using a replication plugin.
    7046        wp_mkdir_p( dirname( $dst_file ) );
    7147
    7248        $dst_file = dirname( $dst_file ) . '/' . wp_unique_filename( dirname( $dst_file ), basename( $dst_file ) );
    7349
    74         if ( 'image/png' == $image_type && imagepng( $dst, $dst_file ) )
    75                 return $dst_file;
    76         elseif ( imagejpeg( $dst, $dst_file, apply_filters( 'jpeg_quality', 90, 'wp_crop_image' ) ) )
    77                 return $dst_file;
    78         else
    79                 return false;
     50        $result = $editor->save( $dst_file );
     51        return $dst_file;
    8052}
    8153
    8254/**
    function wp_generate_attachment_metadata( $attachment_id, $file ) { 
    12193
    12294                $sizes = apply_filters( 'intermediate_image_sizes_advanced', $sizes );
    12395
    124                 foreach ($sizes as $size => $size_data ) {
    125                         $resized = image_make_intermediate_size( $file, $size_data['width'], $size_data['height'], $size_data['crop'] );
    126                         if ( $resized )
    127                                 $metadata['sizes'][$size] = $resized;
    128                 }
     96                $editor = WP_Image_Editor::get_instance( $file );
     97                $metadata['sizes'] = $editor->multi_resize( $sizes );
    12998
    13099                // fetch additional metadata from exif/iptc
    131100                $image_meta = wp_read_image_metadata( $file );
  • new file wp-includes/class-wp-image-editor-gd.php

    diff --git wp-includes/class-wp-image-editor-gd.php wp-includes/class-wp-image-editor-gd.php
    new file mode 100644
    index 0000000..94c2eec
    - +  
     1<?php
     2/**
     3 * WordPress GD Image Editor
     4 *
     5 * @package WordPress
     6 * @subpackage Image_Editor
     7 */
     8
     9/**
     10 * WordPress Image Editor Class for Image Manipulation through GD
     11 *
     12 * @since 3.5.0
     13 * @package WordPress
     14 * @subpackage Image_Editor
     15 * @uses WP_Image_Editor Extends class
     16 */
     17class WP_Image_Editor_GD extends WP_Image_Editor {
     18        protected $image = false; // GD Resource
     19
     20        function __destruct() {
     21                if ( $this->image ) {
     22                        // we don't need the original in memory anymore
     23                        imagedestroy( $this->image );
     24                }
     25        }
     26
     27        /**
     28         * Checks to see if current environment supports GD
     29         *
     30         * @since 3.5.0
     31         * @access protected
     32         *
     33         * @return boolean
     34         */
     35        public static function test() {
     36                if ( ! extension_loaded('gd') || ! function_exists('gd_info') )
     37                        return false;
     38
     39                return true;
     40        }
     41
     42        /**
     43         * Loads image from $this->file into new GD Resource
     44         *
     45         * @since 3.5
     46         * @access protected
     47         *
     48         * @return boolean|\WP_Error
     49         */
     50        protected function load() {
     51                if ( $this->image )
     52                        return true;
     53
     54                if ( ! file_exists( $this->file ) )
     55                        return new WP_Error( 'error_loading_image', __('File doesn&#8217;t exist?'), $this->file );
     56
     57                // Set artificially high because GD uses uncompressed images in memory
     58                @ini_set( 'memory_limit', apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT ) );
     59                $this->image = @imagecreatefromstring( file_get_contents( $this->file ) );
     60
     61                if ( ! is_resource( $this->image ) )
     62                        return new WP_Error( 'invalid_image', __('File is not an image.'), $this->file );
     63
     64                $size = @getimagesize( $this->file );
     65                if ( ! $size )
     66                        return new WP_Error( 'invalid_image', __('Could not read image size.'), $this->file );
     67
     68                $this->update_size( $size[0], $size[1] );
     69                $this->mime_type = $size['mime'];
     70
     71                return true;
     72        }
     73
     74        /**
     75         * Sets or updates current image size
     76         *
     77         * @since 3.5.0
     78         * @access protected
     79         *
     80         * @param int $width
     81         * @param int $height
     82         */
     83        protected function update_size( $width = false, $height = false ) {
     84                if ( ! $width )
     85                        $width = imagesx( $this->image );
     86
     87                if ( ! $height )
     88                        $height = imagesy( $this->image );
     89
     90                return parent::update_size( $width, $height );
     91        }
     92
     93        /**
     94         * Checks to see if editor supports mime-type specified
     95         *
     96         * @since 3.5.0
     97         * @access public
     98         *
     99         * @param string $mime_type
     100         * @return boolean
     101         */
     102        public static function supports_mime_type( $mime_type ) {
     103                $allowed_mime_types = array( 'image/gif', 'image/png', 'image/jpeg' );
     104
     105                return in_array( $mime_type, $allowed_mime_types );
     106        }
     107
     108        /**
     109         * Resizes current image.
     110         * Wrapper around _resize, since _resize returns a GD Resource
     111         *
     112         * @param int $max_w
     113         * @param int $max_h
     114         * @param boolean $crop
     115         * @return boolean|WP_Error
     116         */
     117        public function resize( $max_w, $max_h, $crop = false ) {
     118                if ( ( $this->size['width'] == $max_w ) && ( $this->size['height'] == $max_h ) )
     119                        return true;
     120
     121                $resized = $this->_resize( $max_w, $max_h, $crop );
     122
     123                if ( is_resource( $resized ) ) {
     124                        imagedestroy( $this->image );
     125                        $this->image = $resized;
     126                        return true;
     127
     128                } elseif ( is_wp_error( $resized ) )
     129                        return $resized;
     130
     131                return new WP_Error( 'image_resize_error', __('Image resize failed.'), $this->file );
     132        }
     133
     134        protected function _resize( $max_w, $max_h, $crop = false ) {
     135                $dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop );
     136                if ( ! $dims ) {
     137                        return new WP_Error( 'error_getting_dimensions', __('Could not calculate resized image dimensions'), $this->file );
     138                }
     139                list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims;
     140
     141                $resized = wp_imagecreatetruecolor( $dst_w, $dst_h );
     142                imagecopyresampled( $resized, $this->image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h );
     143
     144                if ( is_resource( $resized ) ) {
     145                        $this->update_size( $dst_w, $dst_h );
     146                        return $resized;
     147                }
     148
     149                return WP_Error( 'image_resize_error', __('Image resize failed.'), $this->file );
     150        }
     151
     152        /**
     153         * Processes current image and saves to disk
     154         * multiple sizes from single source.
     155         *
     156         * @param array $sizes { {width, height}, ... }
     157         * @return array
     158         */
     159        public function multi_resize( $sizes ) {
     160                $metadata = array();
     161                $orig_size = $this->size;
     162
     163                foreach ( $sizes as $size => $size_data ) {
     164                        $image = $this->_resize( $size_data['width'], $size_data['height'], $size_data['crop'] );
     165
     166                        if( ! is_wp_error( $image ) ) {
     167                                $resized = $this->_save( $image );
     168
     169                                imagedestroy( $image );
     170                                unset( $resized['path'] );
     171
     172                                if ( ! is_wp_error( $resized ) && $resized )
     173                                        $metadata[$size] = $resized;
     174                        }
     175
     176                        $this->size = $orig_size;
     177                }
     178
     179                return $metadata;
     180        }
     181
     182        /**
     183         * Crops Image.
     184         *
     185         * @since 3.5.0
     186         * @access public
     187         *
     188         * @param string|int $src The source file or Attachment ID.
     189         * @param int $src_x The start x position to crop from.
     190         * @param int $src_y The start y position to crop from.
     191         * @param int $src_w The width to crop.
     192         * @param int $src_h The height to crop.
     193         * @param int $dst_w Optional. The destination width.
     194         * @param int $dst_h Optional. The destination height.
     195         * @param int $src_abs Optional. If the source crop points are absolute.
     196         * @return boolean|WP_Error
     197         */
     198        public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) {
     199                // If destination width/height isn't specified, use same as
     200                // width/height from source.
     201                if ( ! $dst_w )
     202                        $dst_w = $src_w;
     203                if ( ! $dst_h )
     204                        $dst_h = $src_h;
     205
     206                $dst = wp_imagecreatetruecolor( $dst_w, $dst_h );
     207
     208                if ( $src_abs ) {
     209                        $src_w -= $src_x;
     210                        $src_h -= $src_y;
     211                }
     212
     213                if ( function_exists( 'imageantialias' ) )
     214                        imageantialias( $dst, true );
     215
     216                imagecopyresampled( $dst, $this->image, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h );
     217
     218                if ( is_resource( $dst ) ) {
     219                        imagedestroy( $this->image );
     220                        $this->image = $dst;
     221                        $this->update_size( $dst_w, $dst_h );
     222                        return true;
     223                }
     224
     225                return WP_Error( 'image_crop_error', __('Image crop failed.'), $this->file );
     226        }
     227
     228        /**
     229         * Rotates current image counter-clockwise by $angle.
     230         * Ported from image-edit.php
     231         *
     232         * @since 3.5.0
     233         * @access public
     234         *
     235         * @param float $angle
     236         * @return boolean|WP_Error
     237         */
     238        public function rotate( $angle ) {
     239                if ( function_exists('imagerotate') ) {
     240                        $rotated = imagerotate( $this->image, $angle, 0 );
     241
     242                        if ( is_resource( $rotated ) ) {
     243                                imagedestroy( $this->image );
     244                                $this->image = $rotated;
     245                                $this->update_size();
     246                                return true;
     247                        }
     248                }
     249                return WP_Error( 'image_rotate_error', __('Image rotate failed.'), $this->file );
     250        }
     251
     252        /**
     253         * Flips current image
     254         *
     255         * @param boolean $horz Horizonal Flip
     256         * @param boolean $vert Vertical Flip
     257         * @returns boolean|WP_Error
     258         */
     259        public function flip( $horz, $vert ) {
     260                $w = $this->size['width'];
     261                $h = $this->size['height'];
     262                $dst = wp_imagecreatetruecolor( $w, $h );
     263
     264                if ( is_resource( $dst ) ) {
     265                        $sx = $vert ? ($w - 1) : 0;
     266                        $sy = $horz ? ($h - 1) : 0;
     267                        $sw = $vert ? -$w : $w;
     268                        $sh = $horz ? -$h : $h;
     269
     270                        if ( imagecopyresampled( $dst, $this->image, 0, 0, $sx, $sy, $w, $h, $sw, $sh ) ) {
     271                                imagedestroy( $this->image );
     272                                $this->image = $dst;
     273                                return true;
     274                        }
     275                }
     276                return WP_Error( 'image_flip_error', __('Image flip failed.'), $this->file );
     277        }
     278
     279        /**
     280         * Saves current in-memory image to file
     281         *
     282         * @param string $destfilename
     283         * @param string $mime_type
     284         * @return array|WP_Error {'path'=>string, 'file'=>string, 'width'=>int, 'height'=>int, 'mime-type'=>string}
     285         */
     286        public function save( $filename = null, $mime_type = null ) {
     287                $saved = $this->_save( $this->image, $filename, $mime_type );
     288
     289                if ( ! is_wp_error( $saved ) ) {
     290                        $this->file = $saved['path'];
     291                        $this->mime_type = $saved['mime-type'];
     292                }
     293
     294                return $saved;
     295        }
     296
     297        protected function _save( $image, $filename = null, $mime_type = null ) {
     298                list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
     299
     300                if ( ! $filename )
     301                        $filename = $this->generate_filename( null, null, $extension );
     302
     303                if ( 'image/gif' == $mime_type ) {
     304                        if ( ! $this->make_image( $filename, 'imagegif', array( $image, $filename ) ) )
     305                                return new WP_Error( 'image_save_error', __('Image Editor Save Failed') );
     306                }
     307                elseif ( 'image/png' == $mime_type ) {
     308                        // convert from full colors to index colors, like original PNG.
     309                        if ( function_exists('imageistruecolor') && ! imageistruecolor( $image ) )
     310                                imagetruecolortopalette( $image, false, imagecolorstotal( $image ) );
     311
     312                        if ( ! $this->make_image( $filename, 'imagepng', array( $image, $filename ) ) )
     313                                return new WP_Error( 'image_save_error', __('Image Editor Save Failed') );
     314                }
     315                elseif ( 'image/jpeg' == $mime_type ) {
     316                        if ( ! $this->make_image( $filename, 'imagejpeg', array( $image, $filename, apply_filters( 'jpeg_quality', $this->quality, 'image_resize' ) ) ) )
     317                                return new WP_Error( 'image_save_error', __('Image Editor Save Failed') );
     318                }
     319                else {
     320                        return new WP_Error( 'image_save_error', __('Image Editor Save Failed') );
     321                }
     322
     323                // Set correct file permissions
     324                $stat = stat( dirname( $filename ) );
     325                $perms = $stat['mode'] & 0000666; //same permissions as parent folder, strip off the executable bits
     326                @ chmod( $filename, $perms );
     327
     328                return array(
     329                        'path' => $filename,
     330                        'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ),
     331                        'width' => $this->size['width'],
     332                        'height' => $this->size['height'],
     333                        'mime-type'=> $mime_type,
     334                );
     335        }
     336
     337        /**
     338         * Returns stream of current image
     339         *
     340         * @param string $mime_type
     341         */
     342        public function stream( $mime_type = null ) {
     343                list( $filename, $extension, $mime_type ) = $this->get_output_format( null, $mime_type );
     344
     345                switch ( $mime_type ) {
     346                        case 'image/png':
     347                                header( 'Content-Type: image/png' );
     348                                return imagepng( $this->image );
     349                        case 'image/gif':
     350                                header( 'Content-Type: image/gif' );
     351                                return imagegif( $this->image );
     352                        default:
     353                                header( 'Content-Type: image/jpeg' );
     354                                return imagejpeg( $this->image, null, $this->quality );
     355                }
     356        }
     357}
     358 No newline at end of file
  • new file wp-includes/class-wp-image-editor-imagick.php

    diff --git wp-includes/class-wp-image-editor-imagick.php wp-includes/class-wp-image-editor-imagick.php
    new file mode 100644
    index 0000000..6370a53
    - +  
     1<?php
     2/**
     3 * WordPress Imagick Image Editor
     4 *
     5 * @package WordPress
     6 * @subpackage Image_Editor
     7 */
     8
     9/**
     10 * WordPress Image Editor Class for Image Manipulation through Imagick PHP Module
     11 *
     12 * @since 3.5.0
     13 * @package WordPress
     14 * @subpackage Image_Editor
     15 * @uses WP_Image_Editor Extends class
     16 */
     17class WP_Image_Editor_Imagick extends WP_Image_Editor {
     18        protected $image = null; // Imagick Object
     19
     20        function __destruct() {
     21                if ( $this->image ) {
     22                        // we don't need the original in memory anymore
     23                        $this->image->clear();
     24                        $this->image->destroy();
     25                }
     26        }
     27
     28        /**
     29         * Checks to see if current environment supports Imagick
     30         *
     31         * @since 3.5.0
     32         * @access protected
     33         *
     34         * @return boolean
     35         */
     36        public static function test() {
     37                if ( ! extension_loaded( 'imagick' ) )
     38                        return false;
     39
     40                return true;
     41        }
     42
     43        /**
     44         * Loads image from $this->file into new Imagick Object
     45         *
     46         * @since 3.5.0
     47         * @access protected
     48         *
     49         * @return boolean|WP_Error True if loaded; WP_Error on failure.
     50         */
     51        protected function load() {
     52                if ( $this->image )
     53                        return true;
     54
     55                if ( ! file_exists( $this->file ) )
     56                        return new WP_Error( 'error_loading_image', __('File doesn&#8217;t exist?'), $this->file );
     57
     58                try {
     59                        $this->image = new Imagick( $this->file );
     60
     61                        if( ! $this->image->valid() )
     62                                return new WP_Error( 'invalid_image', __('File is not an image.'), $this->file);
     63
     64                        // Select the first frame to handle animated GIFs properly
     65                        $this->image->setIteratorIndex(0);
     66                        $this->mime_type = $this->get_mime_type( $this->image->getImageFormat() );
     67                }
     68                catch ( Exception $e ) {
     69                        return new WP_Error( 'invalid_image', $e->getMessage(), $this->file );
     70                }
     71
     72                $updated_size = $this->update_size();
     73                if ( is_wp_error( $updated_size ) )
     74                                return $updated_size;
     75
     76                return $this->set_quality();
     77        }
     78
     79        /**
     80         * Sets Image Compression quality on a 1-100% scale.
     81         *
     82         * @since 3.5.0
     83         * @access public
     84         *
     85         * @param int $quality Compression Quality. Range: [1,100]
     86         * @return boolean|WP_Error
     87         */
     88        public function set_quality( $quality = null ) {
     89                if ( !$quality )
     90                        $quality = $this->quality;
     91
     92                try {
     93                        if( 'image/jpeg' == $this->mime_type ) {
     94                                $this->image->setImageCompressionQuality( apply_filters( 'jpeg_quality', $quality, 'image_resize' ) );
     95                                $this->image->setImageCompression( imagick::COMPRESSION_JPEG );
     96                        }
     97                        else {
     98                                $this->image->setImageCompressionQuality( $quality );
     99                        }
     100                }
     101                catch ( Exception $e ) {
     102                        return new WP_Error( 'image_quality_error', $e->getMessage() );
     103                }
     104
     105                return parent::set_quality( $quality );
     106        }
     107
     108        /**
     109         * Sets or updates current image size
     110         *
     111         * @since 3.5.0
     112         * @access protected
     113         *
     114         * @param int $width
     115         * @param int $height
     116         */
     117        protected function update_size( $width = null, $height = null ) {
     118                $size = null;
     119                if ( !$width || !$height ) {
     120                        try {
     121                                $size = $this->image->getImageGeometry();
     122                        }
     123                        catch ( Exception $e ) {
     124                                return new WP_Error( 'invalid_image', __('Could not read image size'), $this->file );
     125                        }
     126                }
     127
     128                if ( ! $width )
     129                        $width = $size['width'];
     130
     131                if ( ! $height )
     132                        $height = $size['height'];
     133
     134                return parent::update_size( $width, $height );
     135        }
     136
     137        /**
     138         * Checks to see if editor supports mime-type specified
     139         *
     140         * @since 3.5.0
     141         * @access public
     142         *
     143         * @param string $mime_type
     144         * @return boolean
     145         */
     146        public static function supports_mime_type( $mime_type = null ) {
     147                if ( ! $mime_type )
     148                        return false;
     149
     150                $imagick_extension = strtoupper( self::get_extension( $mime_type ) );
     151
     152                try {
     153                        return ( (bool) Imagick::queryFormats( $imagick_extension ) );
     154                }
     155                catch ( Exception $e ) {
     156                        return false;
     157                }
     158        }
     159
     160        /**
     161         * Resizes current image.
     162         *
     163         * @param int $max_w
     164         * @param int $max_h
     165         * @param boolean $crop
     166         * @return boolean|WP_Error
     167         */
     168        public function resize( $max_w, $max_h, $crop = false ) {
     169                if ( ( $this->size['width'] == $max_w ) && ( $this->size['height'] == $max_h ) )
     170                        return true;
     171
     172                $dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop );
     173                if ( ! $dims )
     174                        return new WP_Error( 'error_getting_dimensions', __('Could not calculate resized image dimensions') );
     175                list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims;
     176
     177                if ( $crop ) {
     178                        return $this->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h );
     179                }
     180
     181                try {
     182                        /**
     183                         * @TODO: Thumbnail is more efficient, given a newer version of Imagemagick.
     184                         * $this->image->thumbnailImage( $dst_w, $dst_h );
     185                         */
     186                        $this->image->scaleImage( $dst_w, $dst_h );
     187                }
     188                catch ( Exception $e ) {
     189                        return new WP_Error( 'image_resize_error', $e->getMessage() );
     190                }
     191
     192                return $this->update_size( $dst_w, $dst_h );
     193        }
     194
     195        /**
     196         * Processes current image and saves to disk
     197         * multiple sizes from single source.
     198         *
     199         * @param array $sizes
     200         * @return array
     201         */
     202        public function multi_resize( $sizes ) {
     203                $metadata = array();
     204                $orig_size = $this->size;
     205                $orig_image = $this->image->getImage();
     206
     207                foreach ( $sizes as $size => $size_data ) {
     208                        if ( ! $this->image )
     209                                $this->image = $orig_image->getImage();
     210
     211                        $resize_result = $this->resize( $size_data['width'], $size_data['height'], $size_data['crop'] );
     212
     213                        if( ! is_wp_error( $resize_result ) ) {
     214                                $resized = $this->_save( $this->image );
     215
     216                                $this->image->clear();
     217                                $this->image->destroy();
     218                                $this->image = null;
     219                                unset( $resized['path'] );
     220
     221                                if ( ! is_wp_error( $resized ) && $resized )
     222                                        $metadata[$size] = $resized;
     223                        }
     224
     225                        $this->size = $orig_size;
     226                }
     227
     228                $this->image = $orig_image;
     229
     230                return $metadata;
     231        }
     232
     233        /**
     234         * Crops Image.
     235         *
     236         * @since 3.5.0
     237         * @access public
     238         *
     239         * @param string|int $src The source file or Attachment ID.
     240         * @param int $src_x The start x position to crop from.
     241         * @param int $src_y The start y position to crop from.
     242         * @param int $src_w The width to crop.
     243         * @param int $src_h The height to crop.
     244         * @param int $dst_w Optional. The destination width.
     245         * @param int $dst_h Optional. The destination height.
     246         * @param int $src_abs Optional. If the source crop points are absolute.
     247         * @return boolean|WP_Error
     248         */
     249        public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) {
     250                // Not sure this is compatible.
     251                if ( $src_abs ) {
     252                        $src_w -= $src_x;
     253                        $src_h -= $src_y;
     254                }
     255
     256                try {
     257                        $this->image->cropImage( $src_w, $src_h, $src_x, $src_y );
     258                        $this->image->setImagePage( $src_w, $src_h, 0, 0);
     259
     260                        if ( $dst_w || $dst_h ) {
     261                                // If destination width/height isn't specified, use same as
     262                                // width/height from source.
     263                                if ( ! $dst_w )
     264                                        $dst_w = $src_w;
     265                                if ( ! $dst_h )
     266                                        $dst_h = $src_h;
     267
     268                                $this->image->scaleImage( $dst_w, $dst_h );
     269                                return $this->update_size( $dst_w, $dst_h );
     270                        }
     271                }
     272                catch ( Exception $e ) {
     273                        return new WP_Error( 'image_crop_error', $e->getMessage() );
     274                }
     275                return $this->update_size( $src_w, $src_h );
     276        }
     277
     278        /**
     279         * Rotates current image counter-clockwise by $angle.
     280         *
     281         * @since 3.5.0
     282         * @access public
     283         *
     284         * @param float $angle
     285         * @return boolean|WP_Error
     286         */
     287        public function rotate( $angle ) {
     288                /**
     289                 * $angle is 360-$angle because Imagick rotates clockwise
     290                 * (GD rotates counter-clockwise)
     291                 */
     292                try {
     293                        $this->image->rotateImage( new ImagickPixel('none'), 360-$angle );
     294                }
     295                catch ( Exception $e ) {
     296                        return new WP_Error( 'image_rotate_error', $e->getMessage() );
     297                }
     298                return $this->update_size();
     299        }
     300
     301        /**
     302         * Flips current image
     303         *
     304         * @since 3.5.0
     305         * @access public
     306         *
     307         * @param boolean $horz Horizontal Flip
     308         * @param boolean $vert Vertical Flip
     309         * @returns boolean
     310         */
     311        public function flip( $horz, $vert ) {
     312                try {
     313                        if ( $horz )
     314                                $this->image->flipImage();
     315
     316                        if ( $vert )
     317                                $this->image->flopImage();
     318                }
     319                catch ( Exception $e ) {
     320                        return new WP_Error( 'image_flip_error', $e->getMessage() );
     321                }
     322                return true;
     323        }
     324
     325        /**
     326         * Saves current image to file
     327         *
     328         * @param string $destfilename
     329         * @param string $mime_type
     330         * @return array|WP_Error {'path'=>string, 'file'=>string, 'width'=>int, 'height'=>int, 'mime-type'=>string}
     331         */
     332        public function save( $destfilename = null, $mime_type = null ) {
     333                $saved = $this->_save( $this->image, $destfilename, $mime_type );
     334
     335                if ( ! is_wp_error( $saved ) ) {
     336                        $this->file = $saved['path'];
     337                        $this->mime_type = $saved['mime-type'];
     338
     339                        try {
     340                                $this->image->setImageFormat( strtoupper( $this->get_extension( $this->mime_type ) ) );
     341                        }
     342                        catch ( Exception $e ) {
     343                                return new WP_Error( 'image_save_error', $e->getMessage(), $this->file );
     344                        }
     345                }
     346
     347                return $saved;
     348        }
     349
     350        protected function _save( $image, $filename = null, $mime_type = null ) {
     351                list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
     352
     353                if ( ! $filename )
     354                        $filename = $this->generate_filename( null, null, $extension );
     355
     356                try {
     357                        // Store initial Format
     358                        $orig_format = $this->image->getImageFormat();
     359
     360                        $this->image->setImageFormat( strtoupper( $this->get_extension( $mime_type ) ) );
     361                        $this->make_image( $filename, array( $image, 'writeImage' ), array( $filename ) );
     362
     363                        // Reset original Format
     364                        $this->image->setImageFormat( $orig_format );
     365                }
     366                catch ( Exception $e ) {
     367                        return new WP_Error( 'image_save_error', $e->getMessage(), $filename );
     368                }
     369
     370                // Set correct file permissions
     371                $stat = stat( dirname( $filename ) );
     372                $perms = $stat['mode'] & 0000666; //same permissions as parent folder, strip off the executable bits
     373                @ chmod( $filename, $perms );
     374
     375                return array(
     376                        'path' => $filename,
     377                        'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ),
     378                        'width' => $this->size['width'],
     379                        'height' => $this->size['height'],
     380                        'mime-type' => $mime_type,
     381                );
     382        }
     383
     384        /**
     385         * Streams current image to browser
     386         *
     387         * @param string $mime_type
     388         * @return boolean|WP_Error
     389         */
     390        public function stream( $mime_type = null ) {
     391                list( $filename, $extension, $mime_type ) = $this->get_output_format( null, $mime_type );
     392
     393                try {
     394                        // Temporarily change format for stream
     395                        $this->image->setImageFormat( strtoupper( $extension ) );
     396
     397                        // Output stream of image content
     398                        header( "Content-Type: $mime_type" );
     399                        print $this->image->getImageBlob();
     400
     401                        // Reset Image to original Format
     402                        $this->image->setImageFormat( $this->get_extension( $this->mime_type ) );
     403                }
     404                catch ( Exception $e ) {
     405                        return new WP_Error( 'image_stream_error', $e->getMessage() );
     406                }
     407
     408                return true;
     409        }
     410}
     411 No newline at end of file
  • new file wp-includes/class-wp-image-editor.php

    diff --git wp-includes/class-wp-image-editor.php wp-includes/class-wp-image-editor.php
    new file mode 100644
    index 0000000..27df919
    - +  
     1<?php
     2/**
     3 * Base WordPress Image Editor
     4 *
     5 * @package WordPress
     6 * @subpackage Image_Editor
     7 */
     8
     9/**
     10 * Base WordPress Image Editor class for which Editor implementations extend
     11 *
     12 * @since 3.5.0
     13 */
     14abstract class WP_Image_Editor {
     15        protected $file = null;
     16        protected $size = null;
     17        protected $mime_type  = null;
     18        protected $default_mime_type = 'image/jpeg';
     19        protected $quality = 90;
     20        private static $implementation;
     21
     22        protected function __construct( $filename ) {
     23                $this->file = $filename;
     24        }
     25
     26        /**
     27         * Returns a WP_Image_Editor instance and loads file into it.
     28         *
     29         * @since 3.5.0
     30         * @access public
     31         *
     32         * @param string $path Path to File to Load
     33         * @return WP_Image_Editor|WP_Error|boolean
     34         */
     35        public final static function get_instance( $path = null ) {
     36                $implementation = apply_filters( 'image_editor_class', self::choose_implementation(), $path );
     37
     38                if ( $implementation ) {
     39                        $editor = new $implementation( $path );
     40                        $loaded = $editor->load();
     41
     42                        if ( is_wp_error ( $loaded ) )
     43                                return $loaded;
     44
     45                        return $editor;
     46                }
     47
     48                return false;
     49        }
     50
     51        /**
     52         * Tests which editors are capable of supporting the request.
     53         *
     54         * @since 3.5.0
     55         * @access private
     56         *
     57         * @return string|bool Class name for the first editor that claims to support the request. False if no editor claims to support the request.
     58         */
     59        private final static function choose_implementation() {
     60
     61                if ( null === self::$implementation ) {
     62                        $request_order = apply_filters( 'wp_editors', array( 'imagick', 'gd' ) );
     63
     64                        // Loop over each editor on each request looking for one which will serve this request's needs
     65                        foreach ( $request_order as $editor ) {
     66                                $class = 'WP_Image_Editor_' . $editor;
     67
     68                                // Check to see if this editor is a possibility, calls the editor statically
     69                                if ( ! call_user_func( array( $class, 'test' ) ) )
     70                                        continue;
     71
     72                                self::$implementation = $class;
     73                                break;
     74                        }
     75                }
     76                return self::$implementation;
     77        }
     78
     79        abstract public static function test(); // returns bool
     80        abstract protected function load(); // returns bool|WP_Error
     81        abstract public static function supports_mime_type( $mime_type ); // returns bool
     82        abstract public function resize( $max_w, $max_h, $crop = false );
     83        abstract public function multi_resize( $sizes );
     84        abstract public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false );
     85        abstract public function rotate( $angle );
     86        abstract public function flip( $horz, $vert );
     87        abstract public function save( $destfilename = null, $mime_type = null );
     88        abstract public function stream( $mime_type = null );
     89
     90        /**
     91         * Gets dimensions of image
     92         *
     93         * @since 3.5.0
     94         * @access public
     95         *
     96         * @return array {'width'=>int, 'height'=>int}
     97         */
     98        public function get_size() {
     99                return $this->size;
     100        }
     101
     102        /**
     103         * Sets current image size
     104         *
     105         * @since 3.5.0
     106         * @access protected
     107         *
     108         * @param int $width
     109         * @param int $height
     110         */
     111        protected function update_size( $width = null, $height = null ) {
     112                $this->size = array(
     113                        'width' => $width,
     114                        'height' => $height
     115                );
     116                return true;
     117        }
     118
     119        /**
     120         * Sets Image Compression quality on a 1-100% scale.
     121         *
     122         * @since 3.5.0
     123         * @access public
     124         *
     125         * @param int $quality Compression Quality. Range: [1,100]
     126         * @return boolean
     127         */
     128        public function set_quality( $quality ) {
     129                $this->quality = apply_filters( 'wp_editor_set_quality', $quality );
     130
     131                return ( (bool) $this->quality );
     132        }
     133
     134        /**
     135         * Returns preferred mime-type and extension based on provided
     136         * file's extension and mime, or current file's extension and mime.
     137         *
     138         * Will default to $this->default_mime_type if requested is not supported.
     139         *
     140         * Provides corrected filename only if filename is provided.
     141         *
     142         * @since 3.5.0
     143         * @access protected
     144         *
     145         * @param string $filename
     146         * @param type $mime_type
     147         * @return array { filename|null, extension, mime-type }
     148         */
     149        protected function get_output_format( $filename = null, $mime_type = null ) {
     150                $new_ext = $file_ext = null;
     151                $file_mime = null;
     152
     153                // By default, assume specified type takes priority
     154                if ( $mime_type ) {
     155                        $new_ext = $this->get_extension( $mime_type );
     156                }
     157
     158                if ( $filename ) {
     159                        $file_ext = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) );
     160                        $file_mime = $this->get_mime_type( $file_ext );
     161                }
     162                else {
     163                        // If no file specified, grab editor's current extension and mime-type.
     164                        $file_ext = strtolower( pathinfo( $this->file, PATHINFO_EXTENSION ) );
     165                        $file_mime = $this->mime_type;
     166                }
     167
     168                // Check to see if specified mime-type is the same as type implied by
     169                // file extension.  If so, prefer extension from file.
     170                if ( ! $mime_type || ( $file_mime == $mime_type ) ) {
     171                        $mime_type = $file_mime;
     172                        $new_ext = $file_ext;
     173                }
     174
     175                // Double-check that the mime-type selected is supported by the editor.
     176                // If not, choose a default instead.
     177                if ( ! $this->supports_mime_type( $mime_type ) ) {
     178                        $mime_type = apply_filters( 'image_editor_default_mime_type', $this->default_mime_type );
     179                        $new_ext = $this->get_extension( $mime_type );
     180                }
     181
     182                if ( $filename ) {
     183                        $ext = '';
     184                        $info = pathinfo( $filename );
     185                        $dir  = $info['dirname'];
     186
     187                        if( isset( $info['extension'] ) )
     188                                $ext = $info['extension'];
     189
     190                        $filename = trailingslashit( $dir ) . wp_basename( $filename, ".$ext" ) . ".{$new_ext}";
     191                }
     192
     193                return array( $filename, $new_ext, $mime_type );
     194        }
     195
     196        /**
     197         * Builds an output filename based on current file, and adding proper suffix
     198         *
     199         * @since 3.5.0
     200         * @access public
     201         *
     202         * @param string $suffix
     203         * @param string $dest_path
     204         * @param string $extension
     205         * @return string filename
     206         */
     207        public function generate_filename( $suffix = null, $dest_path = null, $extension = null ) {
     208                // $suffix will be appended to the destination filename, just before the extension
     209                if ( ! $suffix )
     210                        $suffix = $this->get_suffix();
     211
     212                $info = pathinfo( $this->file );
     213                $dir  = $info['dirname'];
     214                $ext  = $info['extension'];
     215
     216                $name = wp_basename( $this->file, ".$ext" );
     217                $new_ext = strtolower( $extension ? $extension : $ext );
     218
     219                if ( ! is_null( $dest_path ) && $_dest_path = realpath( $dest_path ) )
     220                        $dir = $_dest_path;
     221
     222                return trailingslashit( $dir ) . "{$name}-{$suffix}.{$new_ext}";
     223        }
     224
     225        /**
     226         * Builds and returns proper suffix for file based on height and width.
     227         *
     228         * @since 3.5.0
     229         * @access public
     230         *
     231         * @return string suffix
     232         */
     233        public function get_suffix() {
     234                if ( ! $this->get_size() )
     235                        return false;
     236
     237                return "{$this->size['width']}x{$this->size['height']}";
     238        }
     239
     240        /**
     241         * Either calls editor's save function or handles file as a stream.
     242         *
     243         * @since 3.5.0
     244         * @access protected
     245         *
     246         * @param string|stream $filename
     247         * @param callable $function
     248         * @param array $arguments
     249         * @return boolean
     250         */
     251        protected function make_image( $filename, $function, $arguments ) {
     252                $dst_file = $filename;
     253
     254                if ( $stream = wp_is_stream( $filename ) ) {
     255                        $filename = null;
     256                        ob_start();
     257                }
     258
     259                $result = call_user_func_array( $function, $arguments );
     260
     261                if( $result && $stream ) {
     262                        $contents = ob_get_contents();
     263
     264                        $fp = fopen( $dst_file, 'w' );
     265
     266                        if( ! $fp )
     267                                return false;
     268
     269                        fwrite( $fp, $contents );
     270                        fclose( $fp );
     271                }
     272
     273                if( $stream ) {
     274                        ob_end_clean();
     275                }
     276
     277                return $result;
     278        }
     279
     280        /**
     281         * Returns first matched mime-type from extension,
     282         * as mapped from wp_get_mime_types()
     283         *
     284         * @since 3.5.0
     285         * @access protected
     286         *
     287         * @param string $extension
     288         * @return string|boolean
     289         */
     290        protected static function get_mime_type( $extension = null ) {
     291                if ( ! $extension )
     292                        return false;
     293
     294                $mime_types = wp_get_mime_types();
     295                $extensions = array_keys( $mime_types );
     296
     297                foreach( $extensions as $_extension ) {
     298                        if( preg_match("/{$extension}/i", $_extension ) ) {
     299                                return $mime_types[ $_extension ];
     300                        }
     301                }
     302
     303                return false;
     304        }
     305
     306        /**
     307         * Returns first matched extension from Mime-type,
     308         * as mapped from wp_get_mime_types()
     309         *
     310         * @since 3.5.0
     311         * @access protected
     312         *
     313         * @param string $mime_type
     314         * @return string|boolean
     315         */
     316        protected static function get_extension( $mime_type = null ) {
     317                $extensions = explode( '|', array_search( $mime_type, wp_get_mime_types() ) );
     318
     319                if ( empty( $extensions[0] ) )
     320                        return false;
     321
     322                return $extensions[0];
     323        }
     324}
     325 No newline at end of file
  • wp-includes/deprecated.php

    diff --git wp-includes/deprecated.php wp-includes/deprecated.php
    index f589017..a9744e8 100644
    function _get_post_ancestors( &$post ) { 
    32063206}
    32073207
    32083208/**
     3209 * Load an image from a string, if PHP supports it.
     3210 *
     3211 * @since 2.1.0
     3212 * @deprecated 3.5.0
     3213 * @see WP_Image_Editor
     3214 *
     3215 * @param string $file Filename of the image to load.
     3216 * @return resource The resulting image resource on success, Error string on failure.
     3217 */
     3218function wp_load_image( $file ) {
     3219        _deprecated_function( __FUNCTION__, '3.5', 'WP_Image_Editor' );
     3220
     3221        if ( is_numeric( $file ) )
     3222                $file = get_attached_file( $file );
     3223
     3224        if ( ! file_exists( $file ) )
     3225                return sprintf(__('File &#8220;%s&#8221; doesn&#8217;t exist?'), $file);
     3226
     3227        if ( ! function_exists('imagecreatefromstring') )
     3228                return __('The GD image library is not installed.');
     3229
     3230        // Set artificially high because GD uses uncompressed images in memory
     3231        @ini_set( 'memory_limit', apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT ) );
     3232        $image = imagecreatefromstring( file_get_contents( $file ) );
     3233
     3234        if ( !is_resource( $image ) )
     3235                return sprintf(__('File &#8220;%s&#8221; is not an image.'), $file);
     3236
     3237        return $image;
     3238}
     3239
     3240/**
     3241 * Scale down an image to fit a particular size and save a new copy of the image.
     3242 *
     3243 * The PNG transparency will be preserved using the function, as well as the
     3244 * image type. If the file going in is PNG, then the resized image is going to
     3245 * be PNG. The only supported image types are PNG, GIF, and JPEG.
     3246 *
     3247 * Some functionality requires API to exist, so some PHP version may lose out
     3248 * support. This is not the fault of WordPress (where functionality is
     3249 * downgraded, not actual defects), but of your PHP version.
     3250 *
     3251 * @since 2.5.0
     3252 * @deprecated 3.5.0
     3253 * @see WP_Image_Editor
     3254 *
     3255 * @param string $file Image file path.
     3256 * @param int $max_w Maximum width to resize to.
     3257 * @param int $max_h Maximum height to resize to.
     3258 * @param bool $crop Optional. Whether to crop image or resize.
     3259 * @param string $suffix Optional. File suffix.
     3260 * @param string $dest_path Optional. New image file path.
     3261 * @param int $jpeg_quality Optional, default is 90. Image quality percentage.
     3262 * @return mixed WP_Error on failure. String with new destination path.
     3263 */
     3264function image_resize( $file, $max_w, $max_h, $crop = false, $suffix = null, $dest_path = null, $jpeg_quality = 90 ) {
     3265        _deprecated_function( __FUNCTION__, '3.5', 'WP_Image_Editor' );
     3266
     3267        $editor = WP_Image_Editor::get_instance( $file );
     3268        if ( is_wp_error( $editor ) )
     3269                return $editor;
     3270        $editor->set_quality( $jpeg_quality );
     3271
     3272        $resized = $editor->resize( $max_w, $max_h, $crop );
     3273        if ( is_wp_error( $resized ) )
     3274                return $resized;
     3275
     3276        $dest_file = $editor->generate_filename( $suffix, $dest_path );
     3277        $saved = $editor->save( $dest_file );
     3278
     3279        if ( is_wp_error( $saved ) )
     3280                return $saved;
     3281
     3282        return $dest_file;
     3283}
     3284
     3285/**
    32093286 * Retrieve a single post, based on post ID.
    32103287 *
    32113288 * Has categories in 'post_category' property or key. Has tags in 'tags_input'
  • wp-includes/functions.php

    diff --git wp-includes/functions.php wp-includes/functions.php
    index 98f52bc..91700a0 100644
    function wp_get_original_referer() { 
    12951295 * @return bool Whether the path was created. True if path already exists.
    12961296 */
    12971297function wp_mkdir_p( $target ) {
     1298        $wrapper = null;
     1299
     1300        // strip the protocol
     1301        if( wp_is_stream( $target ) ) {
     1302                list( $wrapper, $target ) = explode( '://', $target, 2 );
     1303        }
     1304
    12981305        // from php.net/mkdir user contributed notes
    12991306        $target = str_replace( '//', '/', $target );
    13001307
     1308        // put the wrapper back on the target
     1309        if( $wrapper !== null ) {
     1310                $target = $wrapper . '://' . $target;
     1311        }
     1312
    13011313        // safe mode fails with a trailing slash under certain PHP versions.
    13021314        $target = rtrim($target, '/'); // Use rtrim() instead of untrailingslashit to avoid formatting.php dependency.
    13031315        if ( empty($target) )
    function _device_can_upload() { 
    37503762}
    37513763
    37523764/**
     3765 * Test if a given path is a stream URL
     3766 *
     3767 * @param string $path The resource path or URL
     3768 * @return bool True if the path is a stream URL
     3769 */
     3770function wp_is_stream( $path ) {
     3771        $wrappers = stream_get_wrappers();
     3772        $wrappers_re = '(' . join('|', $wrappers) . ')';
     3773
     3774        return preg_match( "!^$wrappers_re://!", $path ) === 1;
     3775}
     3776
     3777/**
    37533778 * Test if the supplied date is valid for the Gregorian calendar
    37543779 *
    37553780 * @since 3.5.0
  • wp-includes/media.php

    diff --git wp-includes/media.php wp-includes/media.php
    index f36c81c..1fc4383 100644
    function get_image_tag($id, $alt, $title, $align, $size='medium') { 
    236236}
    237237
    238238/**
    239  * Load an image from a string, if PHP supports it.
    240  *
    241  * @since 2.1.0
    242  *
    243  * @param string $file Filename of the image to load.
    244  * @return resource The resulting image resource on success, Error string on failure.
    245  */
    246 function wp_load_image( $file ) {
    247         if ( is_numeric( $file ) )
    248                 $file = get_attached_file( $file );
    249 
    250         if ( ! file_exists( $file ) )
    251                 return sprintf(__('File &#8220;%s&#8221; doesn&#8217;t exist?'), $file);
    252 
    253         if ( ! function_exists('imagecreatefromstring') )
    254                 return __('The GD image library is not installed.');
    255 
    256         // Set artificially high because GD uses uncompressed images in memory
    257         @ini_set( 'memory_limit', apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT ) );
    258         $image = imagecreatefromstring( file_get_contents( $file ) );
    259 
    260         if ( !is_resource( $image ) )
    261                 return sprintf(__('File &#8220;%s&#8221; is not an image.'), $file);
    262 
    263         return $image;
    264 }
    265 
    266 /**
    267239 * Calculates the new dimensions for a downsampled image.
    268240 *
    269241 * If either width or height are empty, no constraint is applied on
    function image_resize_dimensions($orig_w, $orig_h, $dest_w, $dest_h, $crop = fal 
    393365}
    394366
    395367/**
    396  * Scale down an image to fit a particular size and save a new copy of the image.
    397  *
    398  * The PNG transparency will be preserved using the function, as well as the
    399  * image type. If the file going in is PNG, then the resized image is going to
    400  * be PNG. The only supported image types are PNG, GIF, and JPEG.
    401  *
    402  * Some functionality requires API to exist, so some PHP version may lose out
    403  * support. This is not the fault of WordPress (where functionality is
    404  * downgraded, not actual defects), but of your PHP version.
    405  *
    406  * @since 2.5.0
    407  *
    408  * @param string $file Image file path.
    409  * @param int $max_w Maximum width to resize to.
    410  * @param int $max_h Maximum height to resize to.
    411  * @param bool $crop Optional. Whether to crop image or resize.
    412  * @param string $suffix Optional. File suffix.
    413  * @param string $dest_path Optional. New image file path.
    414  * @param int $jpeg_quality Optional, default is 90. Image quality percentage.
    415  * @return mixed WP_Error on failure. String with new destination path.
    416  */
    417 function image_resize( $file, $max_w, $max_h, $crop = false, $suffix = null, $dest_path = null, $jpeg_quality = 90 ) {
    418 
    419         $image = wp_load_image( $file );
    420         if ( !is_resource( $image ) )
    421                 return new WP_Error( 'error_loading_image', $image, $file );
    422 
    423         $size = @getimagesize( $file );
    424         if ( !$size )
    425                 return new WP_Error('invalid_image', __('Could not read image size'), $file);
    426         list($orig_w, $orig_h, $orig_type) = $size;
    427 
    428         $dims = image_resize_dimensions($orig_w, $orig_h, $max_w, $max_h, $crop);
    429         if ( !$dims )
    430                 return new WP_Error( 'error_getting_dimensions', __('Could not calculate resized image dimensions') );
    431         list($dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) = $dims;
    432 
    433         $newimage = wp_imagecreatetruecolor( $dst_w, $dst_h );
    434 
    435         imagecopyresampled( $newimage, $image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
    436 
    437         // convert from full colors to index colors, like original PNG.
    438         if ( IMAGETYPE_PNG == $orig_type && function_exists('imageistruecolor') && !imageistruecolor( $image ) )
    439                 imagetruecolortopalette( $newimage, false, imagecolorstotal( $image ) );
    440 
    441         // we don't need the original in memory anymore
    442         imagedestroy( $image );
    443 
    444         // $suffix will be appended to the destination filename, just before the extension
    445         if ( !$suffix )
    446                 $suffix = "{$dst_w}x{$dst_h}";
    447 
    448         $info = pathinfo($file);
    449         $dir = $info['dirname'];
    450         $ext = $info['extension'];
    451         $name = wp_basename($file, ".$ext");
    452 
    453         if ( !is_null($dest_path) and $_dest_path = realpath($dest_path) )
    454                 $dir = $_dest_path;
    455         $destfilename = "{$dir}/{$name}-{$suffix}.{$ext}";
    456 
    457         if ( IMAGETYPE_GIF == $orig_type ) {
    458                 if ( !imagegif( $newimage, $destfilename ) )
    459                         return new WP_Error('resize_path_invalid', __( 'Resize path invalid' ));
    460         } elseif ( IMAGETYPE_PNG == $orig_type ) {
    461                 if ( !imagepng( $newimage, $destfilename ) )
    462                         return new WP_Error('resize_path_invalid', __( 'Resize path invalid' ));
    463         } else {
    464                 // all other formats are converted to jpg
    465                 if ( 'jpg' != $ext && 'jpeg' != $ext )
    466                         $destfilename = "{$dir}/{$name}-{$suffix}.jpg";
    467                 if ( !imagejpeg( $newimage, $destfilename, apply_filters( 'jpeg_quality', $jpeg_quality, 'image_resize' ) ) )
    468                         return new WP_Error('resize_path_invalid', __( 'Resize path invalid' ));
    469         }
    470 
    471         imagedestroy( $newimage );
    472 
    473         // Set correct file permissions
    474         $stat = stat( dirname( $destfilename ));
    475         $perms = $stat['mode'] & 0000666; //same permissions as parent folder, strip off the executable bits
    476         @ chmod( $destfilename, $perms );
    477 
    478         return $destfilename;
    479 }
    480 
    481 /**
    482368 * Resize an image to make a thumbnail or intermediate size.
    483369 *
    484370 * The returned array has the file size, the image width, and image height. The
    function image_resize( $file, $max_w, $max_h, $crop = false, $suffix = null, $de 
    493379 * @param bool $crop Optional, default is false. Whether to crop image to specified height and width or resize.
    494380 * @return bool|array False, if no image was created. Metadata array on success.
    495381 */
    496 function image_make_intermediate_size($file, $width, $height, $crop=false) {
     382function image_make_intermediate_size( $file, $width, $height, $crop = false ) {
    497383        if ( $width || $height ) {
    498                 $resized_file = image_resize($file, $width, $height, $crop);
    499                 if ( !is_wp_error($resized_file) && $resized_file && $info = getimagesize($resized_file) ) {
    500                         $resized_file = apply_filters('image_make_intermediate_size', $resized_file);
    501                         return array(
    502                                 'file' => wp_basename( $resized_file ),
    503                                 'width' => $info[0],
    504                                 'height' => $info[1],
    505                         );
     384                $editor = WP_Image_Editor::get_instance( $file );
     385
     386                if ( is_wp_error( $editor->resize( $width, $height, $crop ) ) );
     387                        return false;
     388
     389                $resized_file = $editor->save();
     390
     391                if ( ! is_wp_error( $resized_file ) && $resized_file ) {
     392                        unset( $resized_file['path'] );
     393                        return $resized_file;
    506394                }
    507395        }
    508396        return false;
    function gd_edit_image_support($mime_type) { 
    1047935
    1048936/**
    1049937 * Create new GD image resource with transparency support
     938 * @TODO: Deprecate if possible.
    1050939 *
    1051940 * @since 2.9.0
    1052941 *
  • wp-settings.php

    diff --git wp-settings.php wp-settings.php
    index 0b81e4d..470ed9a 100644
    require( ABSPATH . WPINC . '/nav-menu.php' ); 
    143143require( ABSPATH . WPINC . '/nav-menu-template.php' );
    144144require( ABSPATH . WPINC . '/admin-bar.php' );
    145145
     146require( ABSPATH . WPINC . '/class-wp-image-editor.php' );
     147require( ABSPATH . WPINC . '/class-wp-image-editor-gd.php' );
     148require( ABSPATH . WPINC . '/class-wp-image-editor-imagick.php' );
     149
    146150// Load multisite-specific files.
    147151if ( is_multisite() ) {
    148152        require( ABSPATH . WPINC . '/ms-functions.php' );