[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-admin/includes/ -> image-edit.php (source)

   1  <?php
   2  /**
   3   * WordPress Image Editor
   4   *
   5   * @package WordPress
   6   * @subpackage Administration
   7   */
   8  
   9  /**
  10   * Loads the WP image-editing interface.
  11   *
  12   * @since 2.9.0
  13   *
  14   * @param int         $post_id Attachment post ID.
  15   * @param bool|object $msg     Optional. Message to display for image editor updates or errors.
  16   *                             Default false.
  17   */
  18  function wp_image_editor( $post_id, $msg = false ) {
  19      $nonce     = wp_create_nonce( "image_editor-$post_id" );
  20      $meta      = wp_get_attachment_metadata( $post_id );
  21      $thumb     = image_get_intermediate_size( $post_id, 'thumbnail' );
  22      $sub_sizes = isset( $meta['sizes'] ) && is_array( $meta['sizes'] );
  23      $note      = '';
  24  
  25      if ( isset( $meta['width'], $meta['height'] ) ) {
  26          $big = max( $meta['width'], $meta['height'] );
  27      } else {
  28          die( __( 'Image data does not exist. Please re-upload the image.' ) );
  29      }
  30  
  31      $sizer = $big > 400 ? 400 / $big : 1;
  32  
  33      $backup_sizes = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true );
  34      $can_restore  = false;
  35      if ( ! empty( $backup_sizes ) && isset( $backup_sizes['full-orig'], $meta['file'] ) ) {
  36          $can_restore = wp_basename( $meta['file'] ) !== $backup_sizes['full-orig']['file'];
  37      }
  38  
  39      if ( $msg ) {
  40          if ( isset( $msg->error ) ) {
  41              $note = "<div class='notice notice-error' tabindex='-1' role='alert'><p>$msg->error</p></div>";
  42          } elseif ( isset( $msg->msg ) ) {
  43              $note = "<div class='notice notice-success' tabindex='-1' role='alert'><p>$msg->msg</p></div>";
  44          }
  45      }
  46  
  47      ?>
  48      <div class="imgedit-wrap wp-clearfix">
  49      <div id="imgedit-panel-<?php echo $post_id; ?>">
  50  
  51      <div class="imgedit-panel-content wp-clearfix">
  52          <?php echo $note; ?>
  53          <div class="imgedit-menu wp-clearfix">
  54              <button type="button" onclick="imageEdit.handleCropToolClick( <?php echo "$post_id, '$nonce'"; ?>, this )" class="imgedit-crop button disabled" disabled><?php esc_html_e( 'Crop' ); ?></button>
  55              <?php
  56  
  57              // On some setups GD library does not provide imagerotate() - Ticket #11536.
  58              if ( wp_image_editor_supports(
  59                  array(
  60                      'mime_type' => get_post_mime_type( $post_id ),
  61                      'methods'   => array( 'rotate' ),
  62                  )
  63              ) ) {
  64                  $note_no_rotate = '';
  65                  ?>
  66                  <button type="button" class="imgedit-rleft button" onclick="imageEdit.rotate( 90, <?php echo "$post_id, '$nonce'"; ?>, this)"><?php esc_html_e( 'Rotate left' ); ?></button>
  67                  <button type="button" class="imgedit-rright button" onclick="imageEdit.rotate(-90, <?php echo "$post_id, '$nonce'"; ?>, this)"><?php esc_html_e( 'Rotate right' ); ?></button>
  68                  <?php
  69              } else {
  70                  $note_no_rotate = '<p class="note-no-rotate"><em>' . __( 'Image rotation is not supported by your web host.' ) . '</em></p>';
  71                  ?>
  72                  <button type="button" class="imgedit-rleft button disabled" disabled></button>
  73                  <button type="button" class="imgedit-rright button disabled" disabled></button>
  74              <?php } ?>
  75  
  76              <button type="button" onclick="imageEdit.flip(1, <?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-flipv button"><?php esc_html_e( 'Flip vertical' ); ?></button>
  77              <button type="button" onclick="imageEdit.flip(2, <?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-fliph button"><?php esc_html_e( 'Flip horizontal' ); ?></button>
  78  
  79              <br class="imgedit-undo-redo-separator" />
  80              <button type="button" id="image-undo-<?php echo $post_id; ?>" onclick="imageEdit.undo(<?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-undo button disabled" disabled><?php esc_html_e( 'Undo' ); ?></button>
  81              <button type="button" id="image-redo-<?php echo $post_id; ?>" onclick="imageEdit.redo(<?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-redo button disabled" disabled><?php esc_html_e( 'Redo' ); ?></button>
  82              <?php echo $note_no_rotate; ?>
  83          </div>
  84  
  85          <input type="hidden" id="imgedit-sizer-<?php echo $post_id; ?>" value="<?php echo $sizer; ?>" />
  86          <input type="hidden" id="imgedit-history-<?php echo $post_id; ?>" value="" />
  87          <input type="hidden" id="imgedit-undone-<?php echo $post_id; ?>" value="0" />
  88          <input type="hidden" id="imgedit-selection-<?php echo $post_id; ?>" value="" />
  89          <input type="hidden" id="imgedit-x-<?php echo $post_id; ?>" value="<?php echo isset( $meta['width'] ) ? $meta['width'] : 0; ?>" />
  90          <input type="hidden" id="imgedit-y-<?php echo $post_id; ?>" value="<?php echo isset( $meta['height'] ) ? $meta['height'] : 0; ?>" />
  91  
  92          <div id="imgedit-crop-<?php echo $post_id; ?>" class="imgedit-crop-wrap">
  93          <img id="image-preview-<?php echo $post_id; ?>" onload="imageEdit.imgLoaded('<?php echo $post_id; ?>')" src="<?php echo admin_url( 'admin-ajax.php', 'relative' ); ?>?action=imgedit-preview&amp;_ajax_nonce=<?php echo $nonce; ?>&amp;postid=<?php echo $post_id; ?>&amp;rand=<?php echo rand( 1, 99999 ); ?>" alt="" />
  94          </div>
  95  
  96          <div class="imgedit-submit">
  97              <input type="button" onclick="imageEdit.close(<?php echo $post_id; ?>, 1)" class="button imgedit-cancel-btn" value="<?php esc_attr_e( 'Cancel' ); ?>" />
  98              <input type="button" onclick="imageEdit.save(<?php echo "$post_id, '$nonce'"; ?>)" disabled="disabled" class="button button-primary imgedit-submit-btn" value="<?php esc_attr_e( 'Save' ); ?>" />
  99          </div>
 100      </div>
 101  
 102      <div class="imgedit-settings">
 103      <div class="imgedit-group">
 104      <div class="imgedit-group-top">
 105          <h2><?php _e( 'Scale Image' ); ?></h2>
 106          <button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);" aria-expanded="false"><span class="screen-reader-text"><?php esc_html_e( 'Scale Image Help' ); ?></span></button>
 107          <div class="imgedit-help">
 108          <p><?php _e( 'You can proportionally scale the original image. For best results, scaling should be done before you crop, flip, or rotate. Images can only be scaled down, not up.' ); ?></p>
 109          </div>
 110          <?php if ( isset( $meta['width'], $meta['height'] ) ) : ?>
 111          <p>
 112              <?php
 113              printf(
 114                  /* translators: %s: Image width and height in pixels. */
 115                  __( 'Original dimensions %s' ),
 116                  '<span class="imgedit-original-dimensions">' . $meta['width'] . ' &times; ' . $meta['height'] . '</span>'
 117              );
 118              ?>
 119          </p>
 120          <?php endif ?>
 121          <div class="imgedit-submit">
 122  
 123          <fieldset class="imgedit-scale">
 124          <legend><?php _e( 'New dimensions:' ); ?></legend>
 125          <div class="nowrap">
 126          <label for="imgedit-scale-width-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'scale width' ); ?></label>
 127          <input type="text" id="imgedit-scale-width-<?php echo $post_id; ?>" onkeyup="imageEdit.scaleChanged(<?php echo $post_id; ?>, 1, this)" onblur="imageEdit.scaleChanged(<?php echo $post_id; ?>, 1, this)" value="<?php echo isset( $meta['width'] ) ? $meta['width'] : 0; ?>" />
 128          <span class="imgedit-separator" aria-hidden="true">&times;</span>
 129          <label for="imgedit-scale-height-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'scale height' ); ?></label>
 130          <input type="text" id="imgedit-scale-height-<?php echo $post_id; ?>" onkeyup="imageEdit.scaleChanged(<?php echo $post_id; ?>, 0, this)" onblur="imageEdit.scaleChanged(<?php echo $post_id; ?>, 0, this)" value="<?php echo isset( $meta['height'] ) ? $meta['height'] : 0; ?>" />
 131          <span class="imgedit-scale-warn" id="imgedit-scale-warn-<?php echo $post_id; ?>">!</span>
 132          <div class="imgedit-scale-button-wrapper"><input id="imgedit-scale-button" type="button" onclick="imageEdit.action(<?php echo "$post_id, '$nonce'"; ?>, 'scale')" class="button button-primary" value="<?php esc_attr_e( 'Scale' ); ?>" /></div>
 133          </div>
 134          </fieldset>
 135  
 136          </div>
 137      </div>
 138      </div>
 139  
 140      <?php if ( $can_restore ) { ?>
 141  
 142      <div class="imgedit-group">
 143      <div class="imgedit-group-top">
 144          <h2><button type="button" onclick="imageEdit.toggleHelp(this);" class="button-link" aria-expanded="false"><?php _e( 'Restore original image' ); ?> <span class="dashicons dashicons-arrow-down imgedit-help-toggle"></span></button></h2>
 145          <div class="imgedit-help imgedit-restore">
 146          <p>
 147              <?php
 148              _e( 'Discard any changes and restore the original image.' );
 149  
 150              if ( ! defined( 'IMAGE_EDIT_OVERWRITE' ) || ! IMAGE_EDIT_OVERWRITE ) {
 151                  echo ' ' . __( 'Previously edited copies of the image will not be deleted.' );
 152              }
 153              ?>
 154          </p>
 155          <div class="imgedit-submit">
 156          <input type="button" onclick="imageEdit.action(<?php echo "$post_id, '$nonce'"; ?>, 'restore')" class="button button-primary" value="<?php esc_attr_e( 'Restore image' ); ?>" <?php echo $can_restore; ?> />
 157          </div>
 158          </div>
 159      </div>
 160      </div>
 161  
 162      <?php } ?>
 163  
 164      <div class="imgedit-group">
 165      <div class="imgedit-group-top">
 166          <h2><?php _e( 'Image Crop' ); ?></h2>
 167          <button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);" aria-expanded="false"><span class="screen-reader-text"><?php esc_html_e( 'Image Crop Help' ); ?></span></button>
 168  
 169          <div class="imgedit-help">
 170          <p><?php _e( 'To crop the image, click on it and drag to make your selection.' ); ?></p>
 171  
 172          <p><strong><?php _e( 'Crop Aspect Ratio' ); ?></strong><br />
 173          <?php _e( 'The aspect ratio is the relationship between the width and height. You can preserve the aspect ratio by holding down the shift key while resizing your selection. Use the input box to specify the aspect ratio, e.g. 1:1 (square), 4:3, 16:9, etc.' ); ?></p>
 174  
 175          <p><strong><?php _e( 'Crop Selection' ); ?></strong><br />
 176          <?php _e( 'Once you have made your selection, you can adjust it by entering the size in pixels. The minimum selection size is the thumbnail size as set in the Media settings.' ); ?></p>
 177          </div>
 178      </div>
 179  
 180      <fieldset class="imgedit-crop-ratio">
 181          <legend><?php _e( 'Aspect ratio:' ); ?></legend>
 182          <div class="nowrap">
 183          <label for="imgedit-crop-width-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'crop ratio width' ); ?></label>
 184          <input type="text" id="imgedit-crop-width-<?php echo $post_id; ?>" onkeyup="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 0, this)" onblur="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 0, this)" />
 185          <span class="imgedit-separator" aria-hidden="true">:</span>
 186          <label for="imgedit-crop-height-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'crop ratio height' ); ?></label>
 187          <input type="text" id="imgedit-crop-height-<?php echo $post_id; ?>" onkeyup="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 1, this)" onblur="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 1, this)" />
 188          </div>
 189      </fieldset>
 190  
 191      <fieldset id="imgedit-crop-sel-<?php echo $post_id; ?>" class="imgedit-crop-sel">
 192          <legend><?php _e( 'Selection:' ); ?></legend>
 193          <div class="nowrap">
 194          <label for="imgedit-sel-width-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'selection width' ); ?></label>
 195          <input type="text" id="imgedit-sel-width-<?php echo $post_id; ?>" onkeyup="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" onblur="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" />
 196          <span class="imgedit-separator" aria-hidden="true">&times;</span>
 197          <label for="imgedit-sel-height-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'selection height' ); ?></label>
 198          <input type="text" id="imgedit-sel-height-<?php echo $post_id; ?>" onkeyup="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" onblur="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" />
 199          </div>
 200      </fieldset>
 201  
 202      </div>
 203  
 204      <?php
 205      if ( $thumb && $sub_sizes ) {
 206          $thumb_img = wp_constrain_dimensions( $thumb['width'], $thumb['height'], 160, 120 );
 207          ?>
 208  
 209      <div class="imgedit-group imgedit-applyto">
 210      <div class="imgedit-group-top">
 211          <h2><?php _e( 'Thumbnail Settings' ); ?></h2>
 212          <button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);" aria-expanded="false"><span class="screen-reader-text"><?php esc_html_e( 'Thumbnail Settings Help' ); ?></span></button>
 213          <div class="imgedit-help">
 214          <p><?php _e( 'You can edit the image while preserving the thumbnail. For example, you may wish to have a square thumbnail that displays just a section of the image.' ); ?></p>
 215          </div>
 216      </div>
 217  
 218      <figure class="imgedit-thumbnail-preview">
 219          <img src="<?php echo $thumb['url']; ?>" width="<?php echo $thumb_img[0]; ?>" height="<?php echo $thumb_img[1]; ?>" class="imgedit-size-preview" alt="" draggable="false" />
 220          <figcaption class="imgedit-thumbnail-preview-caption"><?php _e( 'Current thumbnail' ); ?></figcaption>
 221      </figure>
 222  
 223      <div id="imgedit-save-target-<?php echo $post_id; ?>" class="imgedit-save-target">
 224      <fieldset>
 225          <legend><?php _e( 'Apply changes to:' ); ?></legend>
 226  
 227          <span class="imgedit-label">
 228              <input type="radio" id="imgedit-target-all" name="imgedit-target-<?php echo $post_id; ?>" value="all" checked="checked" />
 229              <label for="imgedit-target-all"><?php _e( 'All image sizes' ); ?></label>
 230          </span>
 231  
 232          <span class="imgedit-label">
 233              <input type="radio" id="imgedit-target-thumbnail" name="imgedit-target-<?php echo $post_id; ?>" value="thumbnail" />
 234              <label for="imgedit-target-thumbnail"><?php _e( 'Thumbnail' ); ?></label>
 235          </span>
 236  
 237          <span class="imgedit-label">
 238              <input type="radio" id="imgedit-target-nothumb" name="imgedit-target-<?php echo $post_id; ?>" value="nothumb" />
 239              <label for="imgedit-target-nothumb"><?php _e( 'All sizes except thumbnail' ); ?></label>
 240          </span>
 241      </fieldset>
 242      </div>
 243      </div>
 244  
 245      <?php } ?>
 246  
 247      </div>
 248  
 249      </div>
 250      <div class="imgedit-wait" id="imgedit-wait-<?php echo $post_id; ?>"></div>
 251      <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>
 252      </div>
 253      <?php
 254  }
 255  
 256  /**
 257   * Streams image in WP_Image_Editor to browser.
 258   *
 259   * @since 2.9.0
 260   *
 261   * @param WP_Image_Editor $image         The image editor instance.
 262   * @param string          $mime_type     The mime type of the image.
 263   * @param int             $attachment_id The image's attachment post ID.
 264   * @return bool True on success, false on failure.
 265   */
 266  function wp_stream_image( $image, $mime_type, $attachment_id ) {
 267      if ( $image instanceof WP_Image_Editor ) {
 268  
 269          /**
 270           * Filters the WP_Image_Editor instance for the image to be streamed to the browser.
 271           *
 272           * @since 3.5.0
 273           *
 274           * @param WP_Image_Editor $image         The image editor instance.
 275           * @param int             $attachment_id The attachment post ID.
 276           */
 277          $image = apply_filters( 'image_editor_save_pre', $image, $attachment_id );
 278  
 279          if ( is_wp_error( $image->stream( $mime_type ) ) ) {
 280              return false;
 281          }
 282  
 283          return true;
 284      } else {
 285          /* translators: 1: $image, 2: WP_Image_Editor */
 286          _deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( '%1$s needs to be a %2$s object.' ), '$image', 'WP_Image_Editor' ) );
 287  
 288          /**
 289           * Filters the GD image resource to be streamed to the browser.
 290           *
 291           * @since 2.9.0
 292           * @deprecated 3.5.0 Use {@see 'image_editor_save_pre'} instead.
 293           *
 294           * @param resource|GdImage $image         Image resource to be streamed.
 295           * @param int              $attachment_id The attachment post ID.
 296           */
 297          $image = apply_filters_deprecated( 'image_save_pre', array( $image, $attachment_id ), '3.5.0', 'image_editor_save_pre' );
 298  
 299          switch ( $mime_type ) {
 300              case 'image/jpeg':
 301                  header( 'Content-Type: image/jpeg' );
 302                  return imagejpeg( $image, null, 90 );
 303              case 'image/png':
 304                  header( 'Content-Type: image/png' );
 305                  return imagepng( $image );
 306              case 'image/gif':
 307                  header( 'Content-Type: image/gif' );
 308                  return imagegif( $image );
 309              default:
 310                  return false;
 311          }
 312      }
 313  }
 314  
 315  /**
 316   * Saves image to file.
 317   *
 318   * @since 2.9.0
 319   *
 320   * @param string          $filename  Name of the file to be saved.
 321   * @param WP_Image_Editor $image     The image editor instance.
 322   * @param string          $mime_type The mime type of the image.
 323   * @param int             $post_id   Attachment post ID.
 324   * @return bool True on success, false on failure.
 325   */
 326  function wp_save_image_file( $filename, $image, $mime_type, $post_id ) {
 327      if ( $image instanceof WP_Image_Editor ) {
 328  
 329          /** This filter is documented in wp-admin/includes/image-edit.php */
 330          $image = apply_filters( 'image_editor_save_pre', $image, $post_id );
 331  
 332          /**
 333           * Filters whether to skip saving the image file.
 334           *
 335           * Returning a non-null value will short-circuit the save method,
 336           * returning that value instead.
 337           *
 338           * @since 3.5.0
 339           *
 340           * @param bool|null       $override  Value to return instead of saving. Default null.
 341           * @param string          $filename  Name of the file to be saved.
 342           * @param WP_Image_Editor $image     The image editor instance.
 343           * @param string          $mime_type The mime type of the image.
 344           * @param int             $post_id   Attachment post ID.
 345           */
 346          $saved = apply_filters( 'wp_save_image_editor_file', null, $filename, $image, $mime_type, $post_id );
 347  
 348          if ( null !== $saved ) {
 349              return $saved;
 350          }
 351  
 352          return $image->save( $filename, $mime_type );
 353      } else {
 354          /* translators: 1: $image, 2: WP_Image_Editor */
 355          _deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( '%1$s needs to be a %2$s object.' ), '$image', 'WP_Image_Editor' ) );
 356  
 357          /** This filter is documented in wp-admin/includes/image-edit.php */
 358          $image = apply_filters_deprecated( 'image_save_pre', array( $image, $post_id ), '3.5.0', 'image_editor_save_pre' );
 359  
 360          /**
 361           * Filters whether to skip saving the image file.
 362           *
 363           * Returning a non-null value will short-circuit the save method,
 364           * returning that value instead.
 365           *
 366           * @since 2.9.0
 367           * @deprecated 3.5.0 Use {@see 'wp_save_image_editor_file'} instead.
 368           *
 369           * @param mixed           $override  Value to return instead of saving. Default null.
 370           * @param string          $filename  Name of the file to be saved.
 371           * @param WP_Image_Editor $image     The image editor instance.
 372           * @param string          $mime_type The mime type of the image.
 373           * @param int             $post_id   Attachment post ID.
 374           */
 375          $saved = apply_filters_deprecated(
 376              'wp_save_image_file',
 377              array( null, $filename, $image, $mime_type, $post_id ),
 378              '3.5.0',
 379              'wp_save_image_editor_file'
 380          );
 381  
 382          if ( null !== $saved ) {
 383              return $saved;
 384          }
 385  
 386          switch ( $mime_type ) {
 387              case 'image/jpeg':
 388                  /** This filter is documented in wp-includes/class-wp-image-editor.php */
 389                  return imagejpeg( $image, $filename, apply_filters( 'jpeg_quality', 90, 'edit_image' ) );
 390              case 'image/png':
 391                  return imagepng( $image, $filename );
 392              case 'image/gif':
 393                  return imagegif( $image, $filename );
 394              default:
 395                  return false;
 396          }
 397      }
 398  }
 399  
 400  /**
 401   * Image preview ratio. Internal use only.
 402   *
 403   * @since 2.9.0
 404   *
 405   * @ignore
 406   * @param int $w Image width in pixels.
 407   * @param int $h Image height in pixels.
 408   * @return float|int Image preview ratio.
 409   */
 410  function _image_get_preview_ratio( $w, $h ) {
 411      $max = max( $w, $h );
 412      return $max > 400 ? ( 400 / $max ) : 1;
 413  }
 414  
 415  /**
 416   * Returns an image resource. Internal use only.
 417   *
 418   * @since 2.9.0
 419   * @deprecated 3.5.0 Use WP_Image_Editor::rotate()
 420   * @see WP_Image_Editor::rotate()
 421   *
 422   * @ignore
 423   * @param resource|GdImage  $img   Image resource.
 424   * @param float|int         $angle Image rotation angle, in degrees.
 425   * @return resource|GdImage|false GD image resource or GdImage instance, false otherwise.
 426   */
 427  function _rotate_image_resource( $img, $angle ) {
 428      _deprecated_function( __FUNCTION__, '3.5.0', 'WP_Image_Editor::rotate()' );
 429  
 430      if ( function_exists( 'imagerotate' ) ) {
 431          $rotated = imagerotate( $img, $angle, 0 );
 432  
 433          if ( is_gd_image( $rotated ) ) {
 434              imagedestroy( $img );
 435              $img = $rotated;
 436          }
 437      }
 438  
 439      return $img;
 440  }
 441  
 442  /**
 443   * Flips an image resource. Internal use only.
 444   *
 445   * @since 2.9.0
 446   * @deprecated 3.5.0 Use WP_Image_Editor::flip()
 447   * @see WP_Image_Editor::flip()
 448   *
 449   * @ignore
 450   * @param resource|GdImage $img  Image resource or GdImage instance.
 451   * @param bool             $horz Whether to flip horizontally.
 452   * @param bool             $vert Whether to flip vertically.
 453   * @return resource|GdImage (maybe) flipped image resource or GdImage instance.
 454   */
 455  function _flip_image_resource( $img, $horz, $vert ) {
 456      _deprecated_function( __FUNCTION__, '3.5.0', 'WP_Image_Editor::flip()' );
 457  
 458      $w   = imagesx( $img );
 459      $h   = imagesy( $img );
 460      $dst = wp_imagecreatetruecolor( $w, $h );
 461  
 462      if ( is_gd_image( $dst ) ) {
 463          $sx = $vert ? ( $w - 1 ) : 0;
 464          $sy = $horz ? ( $h - 1 ) : 0;
 465          $sw = $vert ? -$w : $w;
 466          $sh = $horz ? -$h : $h;
 467  
 468          if ( imagecopyresampled( $dst, $img, 0, 0, $sx, $sy, $w, $h, $sw, $sh ) ) {
 469              imagedestroy( $img );
 470              $img = $dst;
 471          }
 472      }
 473  
 474      return $img;
 475  }
 476  
 477  /**
 478   * Crops an image resource. Internal use only.
 479   *
 480   * @since 2.9.0
 481   *
 482   * @ignore
 483   * @param resource|GdImage $img Image resource or GdImage instance.
 484   * @param float            $x   Source point x-coordinate.
 485   * @param float            $y   Source point y-coordinate.
 486   * @param float            $w   Source width.
 487   * @param float            $h   Source height.
 488   * @return resource|GdImage (maybe) cropped image resource or GdImage instance.
 489   */
 490  function _crop_image_resource( $img, $x, $y, $w, $h ) {
 491      $dst = wp_imagecreatetruecolor( $w, $h );
 492  
 493      if ( is_gd_image( $dst ) ) {
 494          if ( imagecopy( $dst, $img, 0, 0, $x, $y, $w, $h ) ) {
 495              imagedestroy( $img );
 496              $img = $dst;
 497          }
 498      }
 499  
 500      return $img;
 501  }
 502  
 503  /**
 504   * Performs group of changes on Editor specified.
 505   *
 506   * @since 2.9.0
 507   *
 508   * @param WP_Image_Editor $image   WP_Image_Editor instance.
 509   * @param array           $changes Array of change operations.
 510   * @return WP_Image_Editor WP_Image_Editor instance with changes applied.
 511   */
 512  function image_edit_apply_changes( $image, $changes ) {
 513      if ( is_gd_image( $image ) ) {
 514          /* translators: 1: $image, 2: WP_Image_Editor */
 515          _deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( '%1$s needs to be a %2$s object.' ), '$image', 'WP_Image_Editor' ) );
 516      }
 517  
 518      if ( ! is_array( $changes ) ) {
 519          return $image;
 520      }
 521  
 522      // Expand change operations.
 523      foreach ( $changes as $key => $obj ) {
 524          if ( isset( $obj->r ) ) {
 525              $obj->type  = 'rotate';
 526              $obj->angle = $obj->r;
 527              unset( $obj->r );
 528          } elseif ( isset( $obj->f ) ) {
 529              $obj->type = 'flip';
 530              $obj->axis = $obj->f;
 531              unset( $obj->f );
 532          } elseif ( isset( $obj->c ) ) {
 533              $obj->type = 'crop';
 534              $obj->sel  = $obj->c;
 535              unset( $obj->c );
 536          }
 537          $changes[ $key ] = $obj;
 538      }
 539  
 540      // Combine operations.
 541      if ( count( $changes ) > 1 ) {
 542          $filtered = array( $changes[0] );
 543          for ( $i = 0, $j = 1, $c = count( $changes ); $j < $c; $j++ ) {
 544              $combined = false;
 545              if ( $filtered[ $i ]->type == $changes[ $j ]->type ) {
 546                  switch ( $filtered[ $i ]->type ) {
 547                      case 'rotate':
 548                          $filtered[ $i ]->angle += $changes[ $j ]->angle;
 549                          $combined               = true;
 550                          break;
 551                      case 'flip':
 552                          $filtered[ $i ]->axis ^= $changes[ $j ]->axis;
 553                          $combined              = true;
 554                          break;
 555                  }
 556              }
 557              if ( ! $combined ) {
 558                  $filtered[ ++$i ] = $changes[ $j ];
 559              }
 560          }
 561          $changes = $filtered;
 562          unset( $filtered );
 563      }
 564  
 565      // Image resource before applying the changes.
 566      if ( $image instanceof WP_Image_Editor ) {
 567  
 568          /**
 569           * Filters the WP_Image_Editor instance before applying changes to the image.
 570           *
 571           * @since 3.5.0
 572           *
 573           * @param WP_Image_Editor $image   WP_Image_Editor instance.
 574           * @param array           $changes Array of change operations.
 575           */
 576          $image = apply_filters( 'wp_image_editor_before_change', $image, $changes );
 577      } elseif ( is_gd_image( $image ) ) {
 578  
 579          /**
 580           * Filters the GD image resource before applying changes to the image.
 581           *
 582           * @since 2.9.0
 583           * @deprecated 3.5.0 Use {@see 'wp_image_editor_before_change'} instead.
 584           *
 585           * @param resource|GdImage $image   GD image resource or GdImage instance.
 586           * @param array            $changes Array of change operations.
 587           */
 588          $image = apply_filters_deprecated( 'image_edit_before_change', array( $image, $changes ), '3.5.0', 'wp_image_editor_before_change' );
 589      }
 590  
 591      foreach ( $changes as $operation ) {
 592          switch ( $operation->type ) {
 593              case 'rotate':
 594                  if ( 0 != $operation->angle ) {
 595                      if ( $image instanceof WP_Image_Editor ) {
 596                          $image->rotate( $operation->angle );
 597                      } else {
 598                          $image = _rotate_image_resource( $image, $operation->angle );
 599                      }
 600                  }
 601                  break;
 602              case 'flip':
 603                  if ( 0 != $operation->axis ) {
 604                      if ( $image instanceof WP_Image_Editor ) {
 605                          $image->flip( ( $operation->axis & 1 ) != 0, ( $operation->axis & 2 ) != 0 );
 606                      } else {
 607                          $image = _flip_image_resource( $image, ( $operation->axis & 1 ) != 0, ( $operation->axis & 2 ) != 0 );
 608                      }
 609                  }
 610                  break;
 611              case 'crop':
 612                  $sel = $operation->sel;
 613  
 614                  if ( $image instanceof WP_Image_Editor ) {
 615                      $size = $image->get_size();
 616                      $w    = $size['width'];
 617                      $h    = $size['height'];
 618  
 619                      $scale = 1 / _image_get_preview_ratio( $w, $h ); // Discard preview scaling.
 620                      $image->crop( $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale );
 621                  } else {
 622                      $scale = 1 / _image_get_preview_ratio( imagesx( $image ), imagesy( $image ) ); // Discard preview scaling.
 623                      $image = _crop_image_resource( $image, $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale );
 624                  }
 625                  break;
 626          }
 627      }
 628  
 629      return $image;
 630  }
 631  
 632  
 633  /**
 634   * Streams image in post to browser, along with enqueued changes
 635   * in `$_REQUEST['history']`.
 636   *
 637   * @since 2.9.0
 638   *
 639   * @param int $post_id Attachment post ID.
 640   * @return bool True on success, false on failure.
 641   */
 642  function stream_preview_image( $post_id ) {
 643      $post = get_post( $post_id );
 644  
 645      wp_raise_memory_limit( 'admin' );
 646  
 647      $img = wp_get_image_editor( _load_image_to_edit_path( $post_id ) );
 648  
 649      if ( is_wp_error( $img ) ) {
 650          return false;
 651      }
 652  
 653      $changes = ! empty( $_REQUEST['history'] ) ? json_decode( wp_unslash( $_REQUEST['history'] ) ) : null;
 654      if ( $changes ) {
 655          $img = image_edit_apply_changes( $img, $changes );
 656      }
 657  
 658      // Scale the image.
 659      $size = $img->get_size();
 660      $w    = $size['width'];
 661      $h    = $size['height'];
 662  
 663      $ratio = _image_get_preview_ratio( $w, $h );
 664      $w2    = max( 1, $w * $ratio );
 665      $h2    = max( 1, $h * $ratio );
 666  
 667      if ( is_wp_error( $img->resize( $w2, $h2 ) ) ) {
 668          return false;
 669      }
 670  
 671      return wp_stream_image( $img, $post->post_mime_type, $post_id );
 672  }
 673  
 674  /**
 675   * Restores the metadata for a given attachment.
 676   *
 677   * @since 2.9.0
 678   *
 679   * @param int $post_id Attachment post ID.
 680   * @return stdClass Image restoration message object.
 681   */
 682  function wp_restore_image( $post_id ) {
 683      $meta             = wp_get_attachment_metadata( $post_id );
 684      $file             = get_attached_file( $post_id );
 685      $backup_sizes     = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true );
 686      $old_backup_sizes = $backup_sizes;
 687      $restored         = false;
 688      $msg              = new stdClass;
 689  
 690      if ( ! is_array( $backup_sizes ) ) {
 691          $msg->error = __( 'Cannot load image metadata.' );
 692          return $msg;
 693      }
 694  
 695      $parts         = pathinfo( $file );
 696      $suffix        = time() . rand( 100, 999 );
 697      $default_sizes = get_intermediate_image_sizes();
 698  
 699      if ( isset( $backup_sizes['full-orig'] ) && is_array( $backup_sizes['full-orig'] ) ) {
 700          $data = $backup_sizes['full-orig'];
 701  
 702          if ( $parts['basename'] != $data['file'] ) {
 703              if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE ) {
 704  
 705                  // Delete only if it's an edited image.
 706                  if ( preg_match( '/-e[0-9]{13}\./', $parts['basename'] ) ) {
 707                      wp_delete_file( $file );
 708                  }
 709              } elseif ( isset( $meta['width'], $meta['height'] ) ) {
 710                  $backup_sizes[ "full-$suffix" ] = array(
 711                      'width'  => $meta['width'],
 712                      'height' => $meta['height'],
 713                      'file'   => $parts['basename'],
 714                  );
 715              }
 716          }
 717  
 718          $restored_file = path_join( $parts['dirname'], $data['file'] );
 719          $restored      = update_attached_file( $post_id, $restored_file );
 720  
 721          $meta['file']   = _wp_relative_upload_path( $restored_file );
 722          $meta['width']  = $data['width'];
 723          $meta['height'] = $data['height'];
 724      }
 725  
 726      foreach ( $default_sizes as $default_size ) {
 727          if ( isset( $backup_sizes[ "$default_size-orig" ] ) ) {
 728              $data = $backup_sizes[ "$default_size-orig" ];
 729              if ( isset( $meta['sizes'][ $default_size ] ) && $meta['sizes'][ $default_size ]['file'] != $data['file'] ) {
 730                  if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE ) {
 731  
 732                      // Delete only if it's an edited image.
 733                      if ( preg_match( '/-e[0-9]{13}-/', $meta['sizes'][ $default_size ]['file'] ) ) {
 734                          $delete_file = path_join( $parts['dirname'], $meta['sizes'][ $default_size ]['file'] );
 735                          wp_delete_file( $delete_file );
 736                      }
 737                  } else {
 738                      $backup_sizes[ "$default_size-{$suffix}" ] = $meta['sizes'][ $default_size ];
 739                  }
 740              }
 741  
 742              $meta['sizes'][ $default_size ] = $data;
 743          } else {
 744              unset( $meta['sizes'][ $default_size ] );
 745          }
 746      }
 747  
 748      if ( ! wp_update_attachment_metadata( $post_id, $meta ) ||
 749          ( $old_backup_sizes !== $backup_sizes && ! update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes ) ) ) {
 750  
 751          $msg->error = __( 'Cannot save image metadata.' );
 752          return $msg;
 753      }
 754  
 755      if ( ! $restored ) {
 756          $msg->error = __( 'Image metadata is inconsistent.' );
 757      } else {
 758          $msg->msg = __( 'Image restored successfully.' );
 759      }
 760  
 761      return $msg;
 762  }
 763  
 764  /**
 765   * Saves image to post, along with enqueued changes
 766   * in `$_REQUEST['history']`.
 767   *
 768   * @since 2.9.0
 769   *
 770   * @param int $post_id Attachment post ID.
 771   * @return stdClass
 772   */
 773  function wp_save_image( $post_id ) {
 774      $_wp_additional_image_sizes = wp_get_additional_image_sizes();
 775  
 776      $return  = new stdClass;
 777      $success = false;
 778      $delete  = false;
 779      $scaled  = false;
 780      $nocrop  = false;
 781      $post    = get_post( $post_id );
 782  
 783      $img = wp_get_image_editor( _load_image_to_edit_path( $post_id, 'full' ) );
 784      if ( is_wp_error( $img ) ) {
 785          $return->error = esc_js( __( 'Unable to create new image.' ) );
 786          return $return;
 787      }
 788  
 789      $fwidth  = ! empty( $_REQUEST['fwidth'] ) ? (int) $_REQUEST['fwidth'] : 0;
 790      $fheight = ! empty( $_REQUEST['fheight'] ) ? (int) $_REQUEST['fheight'] : 0;
 791      $target  = ! empty( $_REQUEST['target'] ) ? preg_replace( '/[^a-z0-9_-]+/i', '', $_REQUEST['target'] ) : '';
 792      $scale   = ! empty( $_REQUEST['do'] ) && 'scale' === $_REQUEST['do'];
 793  
 794      if ( $scale && $fwidth > 0 && $fheight > 0 ) {
 795          $size = $img->get_size();
 796          $sX   = $size['width'];
 797          $sY   = $size['height'];
 798  
 799          // Check if it has roughly the same w / h ratio.
 800          $diff = round( $sX / $sY, 2 ) - round( $fwidth / $fheight, 2 );
 801          if ( -0.1 < $diff && $diff < 0.1 ) {
 802              // Scale the full size image.
 803              if ( $img->resize( $fwidth, $fheight ) ) {
 804                  $scaled = true;
 805              }
 806          }
 807  
 808          if ( ! $scaled ) {
 809              $return->error = esc_js( __( 'Error while saving the scaled image. Please reload the page and try again.' ) );
 810              return $return;
 811          }
 812      } elseif ( ! empty( $_REQUEST['history'] ) ) {
 813          $changes = json_decode( wp_unslash( $_REQUEST['history'] ) );
 814          if ( $changes ) {
 815              $img = image_edit_apply_changes( $img, $changes );
 816          }
 817      } else {
 818          $return->error = esc_js( __( 'Nothing to save, the image has not changed.' ) );
 819          return $return;
 820      }
 821  
 822      $meta         = wp_get_attachment_metadata( $post_id );
 823      $backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true );
 824  
 825      if ( ! is_array( $meta ) ) {
 826          $return->error = esc_js( __( 'Image data does not exist. Please re-upload the image.' ) );
 827          return $return;
 828      }
 829  
 830      if ( ! is_array( $backup_sizes ) ) {
 831          $backup_sizes = array();
 832      }
 833  
 834      // Generate new filename.
 835      $path = get_attached_file( $post_id );
 836  
 837      $basename = pathinfo( $path, PATHINFO_BASENAME );
 838      $dirname  = pathinfo( $path, PATHINFO_DIRNAME );
 839      $ext      = pathinfo( $path, PATHINFO_EXTENSION );
 840      $filename = pathinfo( $path, PATHINFO_FILENAME );
 841      $suffix   = time() . rand( 100, 999 );
 842  
 843      if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE &&
 844          isset( $backup_sizes['full-orig'] ) && $backup_sizes['full-orig']['file'] != $basename ) {
 845  
 846          if ( 'thumbnail' === $target ) {
 847              $new_path = "{$dirname}/{$filename}-temp.{$ext}";
 848          } else {
 849              $new_path = $path;
 850          }
 851      } else {
 852          while ( true ) {
 853              $filename     = preg_replace( '/-e([0-9]+)$/', '', $filename );
 854              $filename    .= "-e{$suffix}";
 855              $new_filename = "{$filename}.{$ext}";
 856              $new_path     = "{$dirname}/$new_filename";
 857              if ( file_exists( $new_path ) ) {
 858                  $suffix++;
 859              } else {
 860                  break;
 861              }
 862          }
 863      }
 864  
 865      // Save the full-size file, also needed to create sub-sizes.
 866      if ( ! wp_save_image_file( $new_path, $img, $post->post_mime_type, $post_id ) ) {
 867          $return->error = esc_js( __( 'Unable to save the image.' ) );
 868          return $return;
 869      }
 870  
 871      if ( 'nothumb' === $target || 'all' === $target || 'full' === $target || $scaled ) {
 872          $tag = false;
 873          if ( isset( $backup_sizes['full-orig'] ) ) {
 874              if ( ( ! defined( 'IMAGE_EDIT_OVERWRITE' ) || ! IMAGE_EDIT_OVERWRITE ) && $backup_sizes['full-orig']['file'] !== $basename ) {
 875                  $tag = "full-$suffix";
 876              }
 877          } else {
 878              $tag = 'full-orig';
 879          }
 880  
 881          if ( $tag ) {
 882              $backup_sizes[ $tag ] = array(
 883                  'width'  => $meta['width'],
 884                  'height' => $meta['height'],
 885                  'file'   => $basename,
 886              );
 887          }
 888          $success = ( $path === $new_path ) || update_attached_file( $post_id, $new_path );
 889  
 890          $meta['file'] = _wp_relative_upload_path( $new_path );
 891  
 892          $size           = $img->get_size();
 893          $meta['width']  = $size['width'];
 894          $meta['height'] = $size['height'];
 895  
 896          if ( $success && ( 'nothumb' === $target || 'all' === $target ) ) {
 897              $sizes = get_intermediate_image_sizes();
 898              if ( 'nothumb' === $target ) {
 899                  $sizes = array_diff( $sizes, array( 'thumbnail' ) );
 900              }
 901          }
 902  
 903          $return->fw = $meta['width'];
 904          $return->fh = $meta['height'];
 905      } elseif ( 'thumbnail' === $target ) {
 906          $sizes   = array( 'thumbnail' );
 907          $success = true;
 908          $delete  = true;
 909          $nocrop  = true;
 910      }
 911  
 912      /*
 913       * We need to remove any existing resized image files because
 914       * a new crop or rotate could generate different sizes (and hence, filenames),
 915       * keeping the new resized images from overwriting the existing image files.
 916       * https://core.trac.wordpress.org/ticket/32171
 917       */
 918      if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE && ! empty( $meta['sizes'] ) ) {
 919          foreach ( $meta['sizes'] as $size ) {
 920              if ( ! empty( $size['file'] ) && preg_match( '/-e[0-9]{13}-/', $size['file'] ) ) {
 921                  $delete_file = path_join( $dirname, $size['file'] );
 922                  wp_delete_file( $delete_file );
 923              }
 924          }
 925      }
 926  
 927      if ( isset( $sizes ) ) {
 928          $_sizes = array();
 929  
 930          foreach ( $sizes as $size ) {
 931              $tag = false;
 932              if ( isset( $meta['sizes'][ $size ] ) ) {
 933                  if ( isset( $backup_sizes[ "$size-orig" ] ) ) {
 934                      if ( ( ! defined( 'IMAGE_EDIT_OVERWRITE' ) || ! IMAGE_EDIT_OVERWRITE ) && $backup_sizes[ "$size-orig" ]['file'] != $meta['sizes'][ $size ]['file'] ) {
 935                          $tag = "$size-$suffix";
 936                      }
 937                  } else {
 938                      $tag = "$size-orig";
 939                  }
 940  
 941                  if ( $tag ) {
 942                      $backup_sizes[ $tag ] = $meta['sizes'][ $size ];
 943                  }
 944              }
 945  
 946              if ( isset( $_wp_additional_image_sizes[ $size ] ) ) {
 947                  $width  = (int) $_wp_additional_image_sizes[ $size ]['width'];
 948                  $height = (int) $_wp_additional_image_sizes[ $size ]['height'];
 949                  $crop   = ( $nocrop ) ? false : $_wp_additional_image_sizes[ $size ]['crop'];
 950              } else {
 951                  $height = get_option( "{$size}_size_h" );
 952                  $width  = get_option( "{$size}_size_w" );
 953                  $crop   = ( $nocrop ) ? false : get_option( "{$size}_crop" );
 954              }
 955  
 956              $_sizes[ $size ] = array(
 957                  'width'  => $width,
 958                  'height' => $height,
 959                  'crop'   => $crop,
 960              );
 961          }
 962  
 963          $meta['sizes'] = array_merge( $meta['sizes'], $img->multi_resize( $_sizes ) );
 964      }
 965  
 966      unset( $img );
 967  
 968      if ( $success ) {
 969          wp_update_attachment_metadata( $post_id, $meta );
 970          update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes );
 971  
 972          if ( 'thumbnail' === $target || 'all' === $target || 'full' === $target ) {
 973              // Check if it's an image edit from attachment edit screen.
 974              if ( ! empty( $_REQUEST['context'] ) && 'edit-attachment' === $_REQUEST['context'] ) {
 975                  $thumb_url         = wp_get_attachment_image_src( $post_id, array( 900, 600 ), true );
 976                  $return->thumbnail = $thumb_url[0];
 977              } else {
 978                  $file_url = wp_get_attachment_url( $post_id );
 979                  if ( ! empty( $meta['sizes']['thumbnail'] ) ) {
 980                      $thumb             = $meta['sizes']['thumbnail'];
 981                      $return->thumbnail = path_join( dirname( $file_url ), $thumb['file'] );
 982                  } else {
 983                      $return->thumbnail = "$file_url?w=128&h=128";
 984                  }
 985              }
 986          }
 987      } else {
 988          $delete = true;
 989      }
 990  
 991      if ( $delete ) {
 992          wp_delete_file( $new_path );
 993      }
 994  
 995      $return->msg = esc_js( __( 'Image saved' ) );
 996      return $return;
 997  }


Generated: Tue Oct 27 01:00:08 2020 Cross-referenced by PHPXref 0.7.1