WordPress.org

Make WordPress Core

Ticket #23331: 23331.2.diff

File 23331.2.diff, 12.3 KB (added by markoheijnen, 5 years ago)

Change version number to 3.6.0 and also add [23750] to this patch

  • new file wp-includes/class-wp-image-editor-gmagick.php

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

    diff --git wp-includes/media.php wp-includes/media.php
    index 682ffbb..e0c6357 100644
    function _wp_image_editor_choose( $args = array() ) { 
    12221222        require_once ABSPATH . WPINC . '/class-wp-image-editor.php';
    12231223        require_once ABSPATH . WPINC . '/class-wp-image-editor-gd.php';
    12241224        require_once ABSPATH . WPINC . '/class-wp-image-editor-imagick.php';
     1225        require_once ABSPATH . WPINC . '/class-wp-image-editor-gmagick.php';
    12251226
    12261227        $implementations = apply_filters( 'wp_image_editors',
    1227                 array( 'WP_Image_Editor_Imagick', 'WP_Image_Editor_GD' ) );
     1228                array( 'WP_Image_Editor_Gmagick', 'WP_Image_Editor_Imagick', 'WP_Image_Editor_GD' ) );
    12281229
    12291230        foreach ( $implementations as $implementation ) {
    12301231                if ( ! call_user_func( array( $implementation, 'test' ), $args ) )