Wordpress 3.0
[autoinstalls/wordpress.git] / wp-admin / includes / image-edit.php
1 <?php
2 /**
3  * WordPress Image Editor
4  *
5  * @package WordPress
6  * @subpackage Administration
7  */
8
9 function wp_image_editor($post_id, $msg = false) {
10         $nonce = wp_create_nonce("image_editor-$post_id");
11         $meta = wp_get_attachment_metadata($post_id);
12         $thumb = image_get_intermediate_size($post_id, 'thumbnail');
13         $sub_sizes = isset($meta['sizes']) && is_array($meta['sizes']);
14         $note = '';
15
16         if ( is_array($meta) && isset($meta['width']) )
17                 $big = max( $meta['width'], $meta['height'] );
18         else
19                 die( __('Image data does not exist. Please re-upload the image.') );
20
21         $sizer = $big > 400 ? 400 / $big : 1;
22
23         $backup_sizes = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true );
24         $can_restore = !empty($backup_sizes) && isset($backup_sizes['full-orig'])
25                 && $backup_sizes['full-orig']['file'] != basename($meta['file']);
26
27         if ( $msg ) {
28                 if ( isset($msg->error) )
29                         $note = "<div class='error'><p>$msg->error</p></div>";
30                 elseif ( isset($msg->msg) )
31                         $note = "<div class='updated'><p>$msg->msg</p></div>";
32         }
33
34         ?>
35         <div class="imgedit-wrap">
36         <?php echo $note; ?>
37         <table id="imgedit-panel-<?php echo $post_id; ?>"><tbody>
38         <tr><td>
39         <div class="imgedit-menu">
40                 <div onclick="imageEdit.crop(<?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-crop disabled" title="<?php esc_attr_e( 'Crop' ); ?>"></div><?php
41
42         // On some setups GD library does not provide imagerotate() - Ticket #11536
43         if ( function_exists('imagerotate') ) { ?>
44                 <div class="imgedit-rleft"  onclick="imageEdit.rotate( 90, <?php echo "$post_id, '$nonce'"; ?>, this)" title="<?php esc_attr_e( 'Rotate counter-clockwise' ); ?>"></div>
45                 <div class="imgedit-rright" onclick="imageEdit.rotate(-90, <?php echo "$post_id, '$nonce'"; ?>, this)" title="<?php esc_attr_e( 'Rotate clockwise' ); ?>"></div>
46 <?php } else {
47                 $note_gdlib = esc_attr__('Image rotation is not supported by your web host (function imagerotate() is missing)');
48 ?>
49             <div class="imgedit-rleft disabled"  title="<?php echo $note_gdlib; ?>"></div>
50             <div class="imgedit-rright disabled" title="<?php echo $note_gdlib; ?>"></div>
51 <?php } ?>
52
53                 <div onclick="imageEdit.flip(1, <?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-flipv" title="<?php esc_attr_e( 'Flip vertically' ); ?>"></div>
54                 <div onclick="imageEdit.flip(2, <?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-fliph" title="<?php esc_attr_e( 'Flip horizontally' ); ?>"></div>
55
56                 <div id="image-undo-<?php echo $post_id; ?>" onclick="imageEdit.undo(<?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-undo disabled" title="<?php esc_attr_e( 'Undo' ); ?>"></div>
57                 <div id="image-redo-<?php echo $post_id; ?>" onclick="imageEdit.redo(<?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-redo disabled" title="<?php esc_attr_e( 'Redo' ); ?>"></div>
58                 <br class="clear" />
59         </div>
60
61         <input type="hidden" id="imgedit-sizer-<?php echo $post_id; ?>" value="<?php echo $sizer; ?>" />
62         <input type="hidden" id="imgedit-minthumb-<?php echo $post_id; ?>" value="<?php echo ( get_option('thumbnail_size_w') . ':' . get_option('thumbnail_size_h') ); ?>" />
63         <input type="hidden" id="imgedit-history-<?php echo $post_id; ?>" value="" />
64         <input type="hidden" id="imgedit-undone-<?php echo $post_id; ?>" value="0" />
65         <input type="hidden" id="imgedit-selection-<?php echo $post_id; ?>" value="" />
66         <input type="hidden" id="imgedit-x-<?php echo $post_id; ?>" value="<?php echo $meta['width']; ?>" />
67         <input type="hidden" id="imgedit-y-<?php echo $post_id; ?>" value="<?php echo $meta['height']; ?>" />
68
69         <div id="imgedit-crop-<?php echo $post_id; ?>" class="imgedit-crop-wrap">
70         <img id="image-preview-<?php echo $post_id; ?>" onload="imageEdit.imgLoaded('<?php echo $post_id; ?>')" src="<?php echo admin_url('admin-ajax.php'); ?>?action=imgedit-preview&amp;_ajax_nonce=<?php echo $nonce; ?>&amp;postid=<?php echo $post_id; ?>&amp;rand=<?php echo rand(1, 99999); ?>" />
71         </div>
72
73         <div class="imgedit-submit">
74                 <input type="button" onclick="imageEdit.close(<?php echo $post_id; ?>, 1)" class="button" value="<?php esc_attr_e( 'Cancel' ); ?>" />
75                 <input type="button" onclick="imageEdit.save(<?php echo "$post_id, '$nonce'"; ?>)" disabled="disabled" class="button-primary imgedit-submit-btn" value="<?php esc_attr_e( 'Save' ); ?>" />
76         </div>
77         </td>
78
79         <td class="imgedit-settings">
80         <div class="imgedit-group">
81         <div class="imgedit-group-top">
82                 <a class="imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);return false;" href="#"><strong><?php _e('Scale Image'); ?></strong></a>
83                 <div class="imgedit-help">
84                 <p><?php _e('You can proportionally scale the original image. For best results the scaling should be done before performing any other operations on it like crop, rotate, etc. Note that if you make the image larger it may become fuzzy.'); ?></p>
85                 <p><?php printf( __('Original dimensions %s'), $meta['width'] . '&times;' . $meta['height'] ); ?></p>
86                 <div class="imgedit-submit">
87                 <span class="nowrap"><input type="text" id="imgedit-scale-width-<?php echo $post_id; ?>" onkeyup="imageEdit.scaleChanged(<?php echo $post_id; ?>, 1)" onblur="imageEdit.scaleChanged(<?php echo $post_id; ?>, 1)" style="width:4em;" value="<?php echo $meta['width']; ?>" />&times;<input type="text" id="imgedit-scale-height-<?php echo $post_id; ?>" onkeyup="imageEdit.scaleChanged(<?php echo $post_id; ?>, 0)" onblur="imageEdit.scaleChanged(<?php echo $post_id; ?>, 0)" style="width:4em;" value="<?php echo $meta['height']; ?>" />
88                 <span class="imgedit-scale-warn" id="imgedit-scale-warn-<?php echo $post_id; ?>">!</span></span>
89                 <input type="button" onclick="imageEdit.action(<?php echo "$post_id, '$nonce'"; ?>, 'scale')" class="button-primary" value="<?php esc_attr_e( 'Scale' ); ?>" />
90                 </div>
91                 </div>
92         </div>
93
94 <?php if ( $can_restore ) { ?>
95
96         <div class="imgedit-group-top">
97                 <a class="imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);return false;" href="#"><strong><?php _e('Restore Original Image'); ?></strong></a>
98                 <div class="imgedit-help">
99                 <p><?php _e('Discard any changes and restore the original image.');
100
101                 if ( !defined('IMAGE_EDIT_OVERWRITE') || !IMAGE_EDIT_OVERWRITE )
102                         echo ' '.__('Previously edited copies of the image will not be deleted.');
103
104                 ?></p>
105                 <div class="imgedit-submit">
106                 <input type="button" onclick="imageEdit.action(<?php echo "$post_id, '$nonce'"; ?>, 'restore')" class="button-primary" value="<?php esc_attr_e( 'Restore image' ); ?>" <?php echo $can_restore; ?> />
107                 </div>
108                 </div>
109         </div>
110
111 <?php } ?>
112
113         </div>
114
115         <div class="imgedit-group">
116         <div class="imgedit-group-top">
117                 <strong><?php _e('Image Crop'); ?></strong>
118                 <a class="imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);return false;" href="#"><?php _e('(help)'); ?></a>
119                 <div class="imgedit-help">
120                 <p><?php _e('The image can be cropped by clicking on it and dragging to select the desired part. While dragging the dimensions of the selection are displayed below.'); ?></p>
121                 <strong><?php _e('Keyboard Shortcuts'); ?></strong>
122                 <ul>
123                 <li><?php _e('Arrow: move by 10px'); ?></li>
124                 <li><?php _e('Shift + arrow: move by 1px'); ?></li>
125                 <li><?php _e('Ctrl + arrow: resize by 10px'); ?></li>
126                 <li><?php _e('Ctrl + Shift + arrow: resize by 1px'); ?></li>
127                 <li><?php _e('Shift + drag: lock aspect ratio'); ?></li>
128                 </ul>
129
130                 <p><strong><?php _e('Crop Aspect Ratio'); ?></strong><br />
131                 <?php _e('You can specify the crop selection aspect ratio then hold down the Shift key while dragging to lock it. The values can be 1:1 (square), 4:3, 16:9, etc. If there is a selection, specifying aspect ratio will set it immediately.'); ?></p>
132
133                 <p><strong><?php _e('Crop Selection'); ?></strong><br />
134                 <?php _e('Once started, the selection can be adjusted by entering new values (in pixels). Note that these values are scaled to approximately match the original image dimensions. The minimum selection size equals the thumbnail size as set in the Media settings.'); ?></p>
135                 </div>
136         </div>
137
138         <p>
139                 <?php _e('Aspect ratio:'); ?>
140                 <span  class="nowrap">
141                 <input type="text" id="imgedit-crop-width-<?php echo $post_id; ?>" onkeyup="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 0, this)" style="width:3em;" />
142                 :
143                 <input type="text" id="imgedit-crop-height-<?php echo $post_id; ?>" onkeyup="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 1, this)" style="width:3em;" />
144                 </span>
145         </p>
146
147         <p id="imgedit-crop-sel-<?php echo $post_id; ?>">
148                 <?php _e('Selection:'); ?>
149                 <span  class="nowrap">
150                 <input type="text" id="imgedit-sel-width-<?php echo $post_id; ?>" onkeyup="imageEdit.setNumSelection(<?php echo $post_id; ?>)" style="width:4em;" />
151                 :
152                 <input type="text" id="imgedit-sel-height-<?php echo $post_id; ?>" onkeyup="imageEdit.setNumSelection(<?php echo $post_id; ?>)" style="width:4em;" />
153                 </span>
154         </p>
155         </div>
156
157         <?php if ( $thumb && $sub_sizes ) {
158                 $thumb_img = wp_constrain_dimensions( $thumb['width'], $thumb['height'], 160, 120 );
159         ?>
160
161         <div class="imgedit-group imgedit-applyto">
162         <div class="imgedit-group-top">
163                 <strong><?php _e('Thumbnail Settings'); ?></strong>
164                 <a class="imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);return false;" href="#"><?php _e('(help)'); ?></a>
165                 <p class="imgedit-help"><?php _e('The thumbnail image can be cropped differently. For example it can be square or contain only a portion of the original image to showcase it better. Here you can select whether to apply changes to all image sizes or make the thumbnail different.'); ?></p>
166         </div>
167
168         <p>
169                 <img src="<?php echo $thumb['url']; ?>" width="<?php echo $thumb_img[0]; ?>" height="<?php echo $thumb_img[1]; ?>" class="imgedit-size-preview" alt="" /><br /><?php _e('Current thumbnail'); ?>
170         </p>
171
172         <p id="imgedit-save-target-<?php echo $post_id; ?>">
173                 <strong><?php _e('Apply changes to:'); ?></strong><br />
174
175                 <label class="imgedit-label">
176                 <input type="radio" name="imgedit-target-<?php echo $post_id; ?>" value="all" checked="checked" />
177                 <?php _e('All image sizes'); ?></label>
178
179                 <label class="imgedit-label">
180                 <input type="radio" name="imgedit-target-<?php echo $post_id; ?>" value="thumbnail" />
181                 <?php _e('Thumbnail'); ?></label>
182
183                 <label class="imgedit-label">
184                 <input type="radio" name="imgedit-target-<?php echo $post_id; ?>" value="nothumb" />
185                 <?php _e('All sizes except thumbnail'); ?></label>
186         </p>
187         </div>
188
189         <?php } ?>
190
191         </td></tr>
192         </tbody></table>
193         <div class="imgedit-wait" id="imgedit-wait-<?php echo $post_id; ?>"></div>
194         <script type="text/javascript">imageEdit.init(<?php echo $post_id; ?>);</script>
195         <div class="hidden" id="imgedit-leaving-<?php echo $post_id; ?>"><?php _e("There are unsaved changes that will be lost.  'OK' to continue, 'Cancel' to return to the Image Editor."); ?></div>
196         </div>
197 <?php
198 }
199
200 function load_image_to_edit($post_id, $mime_type, $size = 'full') {
201         $filepath = get_attached_file($post_id);
202
203         if ( $filepath && file_exists($filepath) ) {
204                 if ( 'full' != $size && ( $data = image_get_intermediate_size($post_id, $size) ) )
205                         $filepath = path_join( dirname($filepath), $data['file'] );
206         } elseif ( WP_Http_Fopen::test() ) {
207                 $filepath = wp_get_attachment_url($post_id);
208         }
209
210         $filepath = apply_filters('load_image_to_edit_path', $filepath, $post_id, $size);
211         if ( empty($filepath) )
212                 return false;
213
214         switch ( $mime_type ) {
215                 case 'image/jpeg':
216                         $image = imagecreatefromjpeg($filepath);
217                         break;
218                 case 'image/png':
219                         $image = imagecreatefrompng($filepath);
220                         break;
221                 case 'image/gif':
222                         $image = imagecreatefromgif($filepath);
223                         break;
224                 default:
225                         $image = false;
226                         break;
227         }
228         if ( is_resource($image) ) {
229                 $image = apply_filters('load_image_to_edit', $image, $post_id, $size);
230                 if ( function_exists('imagealphablending') && function_exists('imagesavealpha') ) {
231                         imagealphablending($image, false);
232                         imagesavealpha($image, true);
233                 }
234         }
235         return $image;
236 }
237
238 function wp_stream_image($image, $mime_type, $post_id) {
239         $image = apply_filters('image_save_pre', $image, $post_id);
240
241         switch ( $mime_type ) {
242                 case 'image/jpeg':
243                         header('Content-Type: image/jpeg');
244                         return imagejpeg($image, null, 90);
245                 case 'image/png':
246                         header('Content-Type: image/png');
247                         return imagepng($image);
248                 case 'image/gif':
249                         header('Content-Type: image/gif');
250                         return imagegif($image);
251                 default:
252                         return false;
253         }
254 }
255
256 function wp_save_image_file($filename, $image, $mime_type, $post_id) {
257         $image = apply_filters('image_save_pre', $image, $post_id);
258         $saved = apply_filters('wp_save_image_file', null, $filename, $image, $mime_type, $post_id);
259         if ( null !== $saved )
260                 return $saved;
261
262         switch ( $mime_type ) {
263                 case 'image/jpeg':
264                         return imagejpeg( $image, $filename, apply_filters( 'jpeg_quality', 90, 'edit_image' ) );
265                 case 'image/png':
266                         return imagepng($image, $filename);
267                 case 'image/gif':
268                         return imagegif($image, $filename);
269                 default:
270                         return false;
271         }
272 }
273
274 function _image_get_preview_ratio($w, $h) {
275         $max = max($w, $h);
276         return $max > 400 ? (400 / $max) : 1;
277 }
278
279 function _rotate_image_resource($img, $angle) {
280         if ( function_exists('imagerotate') ) {
281                 $rotated = imagerotate($img, $angle, 0);
282                 if ( is_resource($rotated) ) {
283                         imagedestroy($img);
284                         $img = $rotated;
285                 }
286         }
287         return $img;
288 }
289
290
291 function _flip_image_resource($img, $horz, $vert) {
292         $w = imagesx($img);
293         $h = imagesy($img);
294         $dst = wp_imagecreatetruecolor($w, $h);
295         if ( is_resource($dst) ) {
296                 $sx = $vert ? ($w - 1) : 0;
297                 $sy = $horz ? ($h - 1) : 0;
298                 $sw = $vert ? -$w : $w;
299                 $sh = $horz ? -$h : $h;
300
301                 if ( imagecopyresampled($dst, $img, 0, 0, $sx, $sy, $w, $h, $sw, $sh) ) {
302                         imagedestroy($img);
303                         $img = $dst;
304                 }
305         }
306         return $img;
307 }
308
309 function _crop_image_resource($img, $x, $y, $w, $h) {
310         $dst = wp_imagecreatetruecolor($w, $h);
311         if ( is_resource($dst) ) {
312                 if ( imagecopy($dst, $img, 0, 0, $x, $y, $w, $h) ) {
313                         imagedestroy($img);
314                         $img = $dst;
315                 }
316         }
317         return $img;
318 }
319
320 function image_edit_apply_changes($img, $changes) {
321
322         if ( !is_array($changes) )
323                 return $img;
324
325         // expand change operations
326         foreach ( $changes as $key => $obj ) {
327                 if ( isset($obj->r) ) {
328                         $obj->type = 'rotate';
329                         $obj->angle = $obj->r;
330                         unset($obj->r);
331                 } elseif ( isset($obj->f) ) {
332                         $obj->type = 'flip';
333                         $obj->axis = $obj->f;
334                         unset($obj->f);
335                 } elseif ( isset($obj->c) ) {
336                         $obj->type = 'crop';
337                         $obj->sel = $obj->c;
338                         unset($obj->c);
339                 }
340                 $changes[$key] = $obj;
341         }
342
343         // combine operations
344         if ( count($changes) > 1 ) {
345                 $filtered = array($changes[0]);
346                 for ( $i = 0, $j = 1; $j < count($changes); $j++ ) {
347                         $combined = false;
348                         if ( $filtered[$i]->type == $changes[$j]->type ) {
349                                 switch ( $filtered[$i]->type ) {
350                                         case 'rotate':
351                                                 $filtered[$i]->angle += $changes[$j]->angle;
352                                                 $combined = true;
353                                                 break;
354                                         case 'flip':
355                                                 $filtered[$i]->axis ^= $changes[$j]->axis;
356                                                 $combined = true;
357                                                 break;
358                                 }
359                         }
360                         if ( !$combined )
361                                 $filtered[++$i] = $changes[$j];
362                 }
363                 $changes = $filtered;
364                 unset($filtered);
365         }
366
367         // image resource before applying the changes
368         $img = apply_filters('image_edit_before_change', $img, $changes);
369
370         foreach ( $changes as $operation ) {
371                 switch ( $operation->type ) {
372                         case 'rotate':
373                                 if ( $operation->angle != 0 )
374                                         $img = _rotate_image_resource($img, $operation->angle);
375                                 break;
376                         case 'flip':
377                                 if ( $operation->axis != 0 )
378                                         $img = _flip_image_resource($img, ($operation->axis & 1) != 0, ($operation->axis & 2) != 0);
379                                 break;
380                         case 'crop':
381                                 $sel = $operation->sel;
382                                 $scale = 1 / _image_get_preview_ratio( imagesx($img), imagesy($img) ); // discard preview scaling
383                                 $img = _crop_image_resource($img, $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale);
384                                 break;
385                 }
386         }
387
388         return $img;
389 }
390
391 function stream_preview_image($post_id) {
392         $post = get_post($post_id);
393         @ini_set('memory_limit', '256M');
394         $img = load_image_to_edit( $post_id, $post->post_mime_type, array(400, 400) );
395
396         if ( !is_resource($img) )
397                 return false;
398
399         $changes = !empty($_REQUEST['history']) ? json_decode( stripslashes($_REQUEST['history']) ) : null;
400         if ( $changes )
401                 $img = image_edit_apply_changes($img, $changes);
402
403         // scale the image
404         $w = imagesx($img);
405         $h = imagesy($img);
406         $ratio = _image_get_preview_ratio($w, $h);
407         $w2 = $w * $ratio;
408         $h2 = $h * $ratio;
409
410         $preview = wp_imagecreatetruecolor($w2, $h2);
411         imagecopyresampled( $preview, $img, 0, 0, 0, 0, $w2, $h2, $w, $h );
412         wp_stream_image($preview, $post->post_mime_type, $post_id);
413
414         imagedestroy($preview);
415         imagedestroy($img);
416         return true;
417 }
418
419 function wp_restore_image($post_id) {
420         $meta = wp_get_attachment_metadata($post_id);
421         $file = get_attached_file($post_id);
422         $backup_sizes = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true );
423         $restored = false;
424         $msg = '';
425
426         if ( !is_array($backup_sizes) ) {
427                 $msg->error = __('Cannot load image metadata.');
428                 return $msg;
429         }
430
431         $parts = pathinfo($file);
432         $suffix = time() . rand(100, 999);
433         $default_sizes = apply_filters( 'intermediate_image_sizes', array('large', 'medium', 'thumbnail') );
434
435         if ( isset($backup_sizes['full-orig']) && is_array($backup_sizes['full-orig']) ) {
436                 $data = $backup_sizes['full-orig'];
437
438                 if ( $parts['basename'] != $data['file'] ) {
439                         if ( defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE ) {
440                                 // delete only if it's edited image
441                                 if ( preg_match('/-e[0-9]{13}\./', $parts['basename']) ) {
442                                         $delpath = apply_filters('wp_delete_file', $file);
443                                         @unlink($delpath);
444                                 }
445                         } else {
446                                 $backup_sizes["full-$suffix"] = array('width' => $meta['width'], 'height' => $meta['height'], 'file' => $parts['basename']);
447                         }
448                 }
449
450                 $restored_file = path_join($parts['dirname'], $data['file']);
451                 $restored = update_attached_file($post_id, $restored_file);
452
453                 $meta['file'] = _wp_relative_upload_path( $restored_file );
454                 $meta['width'] = $data['width'];
455                 $meta['height'] = $data['height'];
456                 list ( $uwidth, $uheight ) = wp_constrain_dimensions($meta['width'], $meta['height'], 128, 96);
457                 $meta['hwstring_small'] = "height='$uheight' width='$uwidth'";
458         }
459
460         foreach ( $default_sizes as $default_size ) {
461                 if ( isset($backup_sizes["$default_size-orig"]) ) {
462                         $data = $backup_sizes["$default_size-orig"];
463                         if ( isset($meta['sizes'][$default_size]) && $meta['sizes'][$default_size]['file'] != $data['file'] ) {
464                                 if ( defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE ) {
465                                         // delete only if it's edited image
466                                         if ( preg_match('/-e[0-9]{13}-/', $meta['sizes'][$default_size]['file']) ) {
467                                                 $delpath = apply_filters( 'wp_delete_file', path_join($parts['dirname'], $meta['sizes'][$default_size]['file']) );
468                                                 @unlink($delpath);
469                                         }
470                                 } else {
471                                         $backup_sizes["$default_size-{$suffix}"] = $meta['sizes'][$default_size];
472                                 }
473                         }
474
475                         $meta['sizes'][$default_size] = $data;
476                 } else {
477                         unset($meta['sizes'][$default_size]);
478                 }
479         }
480
481         if ( !wp_update_attachment_metadata($post_id, $meta) || !update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes) ) {
482                 $msg->error = __('Cannot save image metadata.');
483                 return $msg;
484         }
485
486         if ( !$restored )
487                 $msg->error = __('Image metadata is inconsistent.');
488         else
489                 $msg->msg = __('Image restored successfully.');
490
491         return $msg;
492 }
493
494 function wp_save_image($post_id) {
495         $return = '';
496         $success = $delete = $scaled = $nocrop = false;
497         $post = get_post($post_id);
498         @ini_set('memory_limit', '256M');
499         $img = load_image_to_edit($post_id, $post->post_mime_type);
500
501         if ( !is_resource($img) ) {
502                 $return->error = esc_js( __('Unable to create new image.') );
503                 return $return;
504         }
505
506         $fwidth = !empty($_REQUEST['fwidth']) ? intval($_REQUEST['fwidth']) : 0;
507         $fheight = !empty($_REQUEST['fheight']) ? intval($_REQUEST['fheight']) : 0;
508         $target = !empty($_REQUEST['target']) ? preg_replace('/[^a-z0-9_-]+/i', '', $_REQUEST['target']) : '';
509         $scale = !empty($_REQUEST['do']) && 'scale' == $_REQUEST['do'];
510
511         if ( $scale && $fwidth > 0 && $fheight > 0 ) {
512                 $sX = imagesx($img);
513                 $sY = imagesy($img);
514
515                 // check if it has roughly the same w / h ratio
516                 $diff = round($sX / $sY, 2) - round($fwidth / $fheight, 2);
517                 if ( -0.1 < $diff && $diff < 0.1 ) {
518                         // scale the full size image
519                         $dst = wp_imagecreatetruecolor($fwidth, $fheight);
520                         if ( imagecopyresampled( $dst, $img, 0, 0, 0, 0, $fwidth, $fheight, $sX, $sY ) ) {
521                                 imagedestroy($img);
522                                 $img = $dst;
523                                 $scaled = true;
524                         }
525                 }
526
527                 if ( !$scaled ) {
528                         $return->error = esc_js( __('Error while saving the scaled image. Please reload the page and try again.') );
529                         return $return;
530                 }
531         } elseif ( !empty($_REQUEST['history']) ) {
532                 $changes = json_decode( stripslashes($_REQUEST['history']) );
533                 if ( $changes )
534                         $img = image_edit_apply_changes($img, $changes);
535         } else {
536                 $return->error = esc_js( __('Nothing to save, the image has not changed.') );
537                 return $return;
538         }
539
540         $meta = wp_get_attachment_metadata($post_id);
541         $backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true );
542
543         if ( !is_array($meta) ) {
544                 $return->error = esc_js( __('Image data does not exist. Please re-upload the image.') );
545                 return $return;
546         }
547
548         if ( !is_array($backup_sizes) )
549                 $backup_sizes = array();
550
551         // generate new filename
552         $path = get_attached_file($post_id);
553         $path_parts = pathinfo52( $path );
554         $filename = $path_parts['filename'];
555         $suffix = time() . rand(100, 999);
556
557         if ( defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE &&
558                 isset($backup_sizes['full-orig']) && $backup_sizes['full-orig']['file'] != $path_parts['basename'] ) {
559
560                 if ( 'thumbnail' == $target )
561                         $new_path = "{$path_parts['dirname']}/{$filename}-temp.{$path_parts['extension']}";
562                 else
563                         $new_path = $path;
564         } else {
565                 while( true ) {
566                         $filename = preg_replace( '/-e([0-9]+)$/', '', $filename );
567                         $filename .= "-e{$suffix}";
568                         $new_filename = "{$filename}.{$path_parts['extension']}";
569                         $new_path = "{$path_parts['dirname']}/$new_filename";
570                         if ( file_exists($new_path) )
571                                 $suffix++;
572                         else
573                                 break;
574                 }
575         }
576
577         // save the full-size file, also needed to create sub-sizes
578         if ( !wp_save_image_file($new_path, $img, $post->post_mime_type, $post_id) ) {
579                 $return->error = esc_js( __('Unable to save the image.') );
580                 return $return;
581         }
582
583         if ( 'nothumb' == $target || 'all' == $target || 'full' == $target || $scaled ) {
584                 $tag = false;
585                 if ( isset($backup_sizes['full-orig']) ) {
586                         if ( ( !defined('IMAGE_EDIT_OVERWRITE') || !IMAGE_EDIT_OVERWRITE ) && $backup_sizes['full-orig']['file'] != $path_parts['basename'] )
587                                 $tag = "full-$suffix";
588                 } else {
589                         $tag = 'full-orig';
590                 }
591
592                 if ( $tag )
593                         $backup_sizes[$tag] = array('width' => $meta['width'], 'height' => $meta['height'], 'file' => $path_parts['basename']);
594
595                 $success = update_attached_file($post_id, $new_path);
596
597                 $meta['file'] = _wp_relative_upload_path($new_path);
598                 $meta['width'] = imagesx($img);
599                 $meta['height'] = imagesy($img);
600
601                 list ( $uwidth, $uheight ) = wp_constrain_dimensions($meta['width'], $meta['height'], 128, 96);
602                 $meta['hwstring_small'] = "height='$uheight' width='$uwidth'";
603
604                 if ( $success && ('nothumb' == $target || 'all' == $target) ) {
605                         $sizes = apply_filters( 'intermediate_image_sizes', array('large', 'medium', 'thumbnail') );
606                         if ( 'nothumb' == $target )
607                                 $sizes = array_diff( $sizes, array('thumbnail') );
608                 }
609
610                 $return->fw = $meta['width'];
611                 $return->fh = $meta['height'];
612         } elseif ( 'thumbnail' == $target ) {
613                 $sizes = array( 'thumbnail' );
614                 $success = $delete = $nocrop = true;
615         }
616
617         if ( isset($sizes) ) {
618                 foreach ( $sizes as $size ) {
619                         $tag = false;
620                         if ( isset($meta['sizes'][$size]) ) {
621                                 if ( isset($backup_sizes["$size-orig"]) ) {
622                                         if ( ( !defined('IMAGE_EDIT_OVERWRITE') || !IMAGE_EDIT_OVERWRITE ) && $backup_sizes["$size-orig"]['file'] != $meta['sizes'][$size]['file'] )
623                                                 $tag = "$size-$suffix";
624                                 } else {
625                                         $tag = "$size-orig";
626                                 }
627
628                                 if ( $tag )
629                                         $backup_sizes[$tag] = $meta['sizes'][$size];
630                         }
631
632                         $crop = $nocrop ? false : get_option("{$size}_crop");
633                         $resized = image_make_intermediate_size($new_path, get_option("{$size}_size_w"), get_option("{$size}_size_h"), $crop );
634
635                         if ( $resized )
636                                 $meta['sizes'][$size] = $resized;
637                         else
638                                 unset($meta['sizes'][$size]);
639                 }
640         }
641
642         if ( $success ) {
643                 wp_update_attachment_metadata($post_id, $meta);
644                 update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes);
645
646                 if ( $target == 'thumbnail' || $target == 'all' || $target == 'full' ) {
647                         $file_url = wp_get_attachment_url($post_id);
648                         if ( $thumb = $meta['sizes']['thumbnail'] )
649                                 $return->thumbnail = path_join( dirname($file_url), $thumb['file'] );
650                         else
651                                 $return->thumbnail = "$file_url?w=128&h=128";
652                 }
653         } else {
654                 $delete = true;
655         }
656
657         if ( $delete ) {
658                 $delpath = apply_filters('wp_delete_file', $new_path);
659                 @unlink($delpath);
660         }
661
662         imagedestroy($img);
663
664         $return->msg = esc_js( __('Image saved') );
665         return $return;
666 }
667