Ticket #23331: 23331.diff

File 23331.diff, 12.3 KB (added by markoheijnen, 4 months ago)

First try. Code from http://wordpress.org/extend/plugins/gmagick/

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