188 |
188 |
189 /** |
189 /** |
190 * Sets Image Compression quality on a 1-100% scale. |
190 * Sets Image Compression quality on a 1-100% scale. |
191 * |
191 * |
192 * @since 3.5.0 |
192 * @since 3.5.0 |
193 * |
193 * @since 6.8.0 The `$dims` parameter was added. |
194 * @param int $quality Compression Quality. Range: [1,100] |
194 * |
|
195 * @param int $quality Compression Quality. Range: [1,100] |
|
196 * @param array $dims Optional. Image dimensions array with 'width' and 'height' keys. |
195 * @return true|WP_Error True if set successfully; WP_Error on failure. |
197 * @return true|WP_Error True if set successfully; WP_Error on failure. |
196 */ |
198 */ |
197 public function set_quality( $quality = null ) { |
199 public function set_quality( $quality = null, $dims = array() ) { |
198 $quality_result = parent::set_quality( $quality ); |
200 $quality_result = parent::set_quality( $quality, $dims ); |
199 if ( is_wp_error( $quality_result ) ) { |
201 if ( is_wp_error( $quality_result ) ) { |
200 return $quality_result; |
202 return $quality_result; |
201 } else { |
203 } else { |
202 $quality = $this->get_quality(); |
204 $quality = $this->get_quality(); |
203 } |
205 } |
204 |
206 |
205 try { |
207 try { |
206 switch ( $this->mime_type ) { |
208 switch ( $this->mime_type ) { |
207 case 'image/jpeg': |
209 case 'image/jpeg': |
208 $this->image->setImageCompressionQuality( $quality ); |
210 $this->image->setImageCompressionQuality( $quality ); |
|
211 $this->image->setCompressionQuality( $quality ); |
209 $this->image->setImageCompression( imagick::COMPRESSION_JPEG ); |
212 $this->image->setImageCompression( imagick::COMPRESSION_JPEG ); |
210 break; |
213 break; |
211 case 'image/webp': |
214 case 'image/webp': |
212 $webp_info = wp_get_webp_info( $this->file ); |
215 $webp_info = wp_get_webp_info( $this->file ); |
213 |
216 |
214 if ( 'lossless' === $webp_info['type'] ) { |
217 if ( 'lossless' === $webp_info['type'] ) { |
215 // Use WebP lossless settings. |
218 // Use WebP lossless settings. |
216 $this->image->setImageCompressionQuality( 100 ); |
219 $this->image->setImageCompressionQuality( 100 ); |
|
220 $this->image->setCompressionQuality( 100 ); |
217 $this->image->setOption( 'webp:lossless', 'true' ); |
221 $this->image->setOption( 'webp:lossless', 'true' ); |
|
222 parent::set_quality( 100 ); |
218 } else { |
223 } else { |
219 $this->image->setImageCompressionQuality( $quality ); |
224 $this->image->setImageCompressionQuality( $quality ); |
|
225 $this->image->setCompressionQuality( $quality ); |
220 } |
226 } |
221 break; |
227 break; |
222 case 'image/avif': |
228 case 'image/avif': |
|
229 // Set the AVIF encoder to work faster, with minimal impact on image size. |
|
230 $this->image->setOption( 'heic:speed', 7 ); |
|
231 $this->image->setImageCompressionQuality( $quality ); |
|
232 $this->image->setCompressionQuality( $quality ); |
|
233 break; |
223 default: |
234 default: |
224 $this->image->setImageCompressionQuality( $quality ); |
235 $this->image->setImageCompressionQuality( $quality ); |
|
236 $this->image->setCompressionQuality( $quality ); |
225 } |
237 } |
226 } catch ( Exception $e ) { |
238 } catch ( Exception $e ) { |
227 return new WP_Error( 'image_quality_error', $e->getMessage() ); |
239 return new WP_Error( 'image_quality_error', $e->getMessage() ); |
228 } |
240 } |
229 return true; |
241 return true; |
256 if ( ! $height ) { |
268 if ( ! $height ) { |
257 $height = $size['height']; |
269 $height = $size['height']; |
258 } |
270 } |
259 |
271 |
260 /* |
272 /* |
261 * If we still don't have the image size, fall back to `wp_getimagesize`. This ensures AVIF images |
273 * If we still don't have the image size, fall back to `wp_getimagesize`. This ensures AVIF and HEIC images |
262 * are properly sized without affecting previous `getImageGeometry` behavior. |
274 * are properly sized without affecting previous `getImageGeometry` behavior. |
263 */ |
275 */ |
264 if ( ( ! $width || ! $height ) && 'image/avif' === $this->mime_type ) { |
276 if ( ( ! $width || ! $height ) && ( 'image/avif' === $this->mime_type || wp_is_heic_image_mime_type( $this->mime_type ) ) ) { |
265 $size = wp_getimagesize( $this->file ); |
277 $size = wp_getimagesize( $this->file ); |
266 $width = $size[0]; |
278 $width = $size[0]; |
267 $height = $size[1]; |
279 $height = $size[1]; |
268 } |
280 } |
269 |
281 |
334 * @param bool|array $crop { |
346 * @param bool|array $crop { |
335 * Optional. Image cropping behavior. If false, the image will be scaled (default). |
347 * Optional. Image cropping behavior. If false, the image will be scaled (default). |
336 * If true, image will be cropped to the specified dimensions using center positions. |
348 * If true, image will be cropped to the specified dimensions using center positions. |
337 * If an array, the image will be cropped using the array to specify the crop location: |
349 * If an array, the image will be cropped using the array to specify the crop location: |
338 * |
350 * |
339 * @type string $0 The x crop position. Accepts 'left' 'center', or 'right'. |
351 * @type string $0 The x crop position. Accepts 'left', 'center', or 'right'. |
340 * @type string $1 The y crop position. Accepts 'top', 'center', or 'bottom'. |
352 * @type string $1 The y crop position. Accepts 'top', 'center', or 'bottom'. |
341 * } |
353 * } |
342 * @return true|WP_Error |
354 * @return true|WP_Error |
343 */ |
355 */ |
344 public function resize( $max_w, $max_h, $crop = false ) { |
356 public function resize( $max_w, $max_h, $crop = false ) { |
354 list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims; |
366 list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims; |
355 |
367 |
356 if ( $crop ) { |
368 if ( $crop ) { |
357 return $this->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h ); |
369 return $this->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h ); |
358 } |
370 } |
|
371 |
|
372 $this->set_quality( |
|
373 null, |
|
374 array( |
|
375 'width' => $dst_w, |
|
376 'height' => $dst_h, |
|
377 ) |
|
378 ); |
359 |
379 |
360 // Execute the resize. |
380 // Execute the resize. |
361 $thumb_result = $this->thumbnail_image( $dst_w, $dst_h ); |
381 $thumb_result = $this->thumbnail_image( $dst_w, $dst_h ); |
362 if ( is_wp_error( $thumb_result ) ) { |
382 if ( is_wp_error( $thumb_result ) ) { |
363 return $thumb_result; |
383 return $thumb_result; |
462 |
482 |
463 if ( 'image/png' === $this->mime_type ) { |
483 if ( 'image/png' === $this->mime_type ) { |
464 $this->image->setOption( 'png:compression-filter', '5' ); |
484 $this->image->setOption( 'png:compression-filter', '5' ); |
465 $this->image->setOption( 'png:compression-level', '9' ); |
485 $this->image->setOption( 'png:compression-level', '9' ); |
466 $this->image->setOption( 'png:compression-strategy', '1' ); |
486 $this->image->setOption( 'png:compression-strategy', '1' ); |
467 $this->image->setOption( 'png:exclude-chunk', 'all' ); |
487 // Check to see if a PNG is indexed, and find the pixel depth. |
|
488 if ( is_callable( array( $this->image, 'getImageDepth' ) ) ) { |
|
489 $indexed_pixel_depth = $this->image->getImageDepth(); |
|
490 |
|
491 // Indexed PNG files get some additional handling. |
|
492 if ( 0 < $indexed_pixel_depth && 8 >= $indexed_pixel_depth ) { |
|
493 // Check for an alpha channel. |
|
494 if ( |
|
495 is_callable( array( $this->image, 'getImageAlphaChannel' ) ) |
|
496 && $this->image->getImageAlphaChannel() |
|
497 ) { |
|
498 $this->image->setOption( 'png:include-chunk', 'tRNS' ); |
|
499 } else { |
|
500 $this->image->setOption( 'png:exclude-chunk', 'all' ); |
|
501 } |
|
502 |
|
503 // Reduce colors in the images to maximum needed, using the global colorspace. |
|
504 $max_colors = pow( 2, $indexed_pixel_depth ); |
|
505 if ( is_callable( array( $this->image, 'getImageColors' ) ) ) { |
|
506 $current_colors = $this->image->getImageColors(); |
|
507 $max_colors = min( $max_colors, $current_colors ); |
|
508 } |
|
509 $this->image->quantizeImage( $max_colors, $this->image->getColorspace(), 0, false, false ); |
|
510 |
|
511 /** |
|
512 * If the colorspace is 'gray', use the png8 format to ensure it stays indexed. |
|
513 */ |
|
514 if ( Imagick::COLORSPACE_GRAY === $this->image->getImageColorspace() ) { |
|
515 $this->image->setOption( 'png:format', 'png8' ); |
|
516 } |
|
517 } |
|
518 } |
468 } |
519 } |
469 |
520 |
470 /* |
521 /* |
471 * If alpha channel is not defined, set it opaque. |
522 * If alpha channel is not defined, set it opaque. |
472 * |
523 * |
481 if ( $this->image->getImageAlphaChannel() === Imagick::ALPHACHANNEL_UNDEFINED ) { |
532 if ( $this->image->getImageAlphaChannel() === Imagick::ALPHACHANNEL_UNDEFINED ) { |
482 $this->image->setImageAlphaChannel( Imagick::ALPHACHANNEL_OPAQUE ); |
533 $this->image->setImageAlphaChannel( Imagick::ALPHACHANNEL_OPAQUE ); |
483 } |
534 } |
484 } |
535 } |
485 |
536 |
486 // Limit the bit depth of resized images to 8 bits per channel. |
537 // Limit the bit depth of resized images. |
487 if ( is_callable( array( $this->image, 'getImageDepth' ) ) && is_callable( array( $this->image, 'setImageDepth' ) ) ) { |
538 if ( is_callable( array( $this->image, 'getImageDepth' ) ) && is_callable( array( $this->image, 'setImageDepth' ) ) ) { |
488 if ( 8 < $this->image->getImageDepth() ) { |
539 /** |
489 $this->image->setImageDepth( 8 ); |
540 * Filters the maximum bit depth of resized images. |
490 } |
541 * |
|
542 * This filter only applies when resizing using the Imagick editor since GD |
|
543 * does not support getting or setting bit depth. |
|
544 * |
|
545 * Use this to adjust the maximum bit depth of resized images. |
|
546 * |
|
547 * @since 6.8.0 |
|
548 * |
|
549 * @param int $max_depth The maximum bit depth. Default is the input depth. |
|
550 * @param int $image_depth The bit depth of the original image. |
|
551 */ |
|
552 $max_depth = apply_filters( 'image_max_bit_depth', $this->image->getImageDepth(), $this->image->getImageDepth() ); |
|
553 $this->image->setImageDepth( $max_depth ); |
491 } |
554 } |
492 } catch ( Exception $e ) { |
555 } catch ( Exception $e ) { |
493 return new WP_Error( 'image_resize_error', $e->getMessage() ); |
556 return new WP_Error( 'image_resize_error', $e->getMessage() ); |
494 } |
557 } |
495 } |
558 } |