WP_Image_Editor_Imagick::thumbnail_imageprotectedWP 4.5.0

Efficiently resize the current image

This is a WordPress specific implementation of Imagick::thumbnailImage(), which resizes an image to given dimensions and removes any associated profiles.

Метод класса: WP_Image_Editor_Imagick{}

Хуки из метода

Возвращает

null|WP_Error.

Использование

// protected - в коде основоного (родительского) или дочернего класса
$result = $this->thumbnail_image( $dst_w, $dst_h, $filter_name, $strip_meta );
$dst_w(int) (обязательный)
The destination width.
$dst_h(int) (обязательный)
The destination height.
$filter_name(строка)
The Imagick filter to use when resizing.
По умолчанию: 'FILTER_TRIANGLE'
$strip_meta(true|false)
Strip all profiles, excluding color profiles, from the image.
По умолчанию: true

Список изменений

С версии 4.5.0 Введена.

Код WP_Image_Editor_Imagick::thumbnail_image() WP 6.9

protected function thumbnail_image( $dst_w, $dst_h, $filter_name = 'FILTER_TRIANGLE', $strip_meta = true ) {
	$allowed_filters = array(
		'FILTER_POINT',
		'FILTER_BOX',
		'FILTER_TRIANGLE',
		'FILTER_HERMITE',
		'FILTER_HANNING',
		'FILTER_HAMMING',
		'FILTER_BLACKMAN',
		'FILTER_GAUSSIAN',
		'FILTER_QUADRATIC',
		'FILTER_CUBIC',
		'FILTER_CATROM',
		'FILTER_MITCHELL',
		'FILTER_LANCZOS',
		'FILTER_BESSEL',
		'FILTER_SINC',
	);

	/**
	 * Set the filter value if '$filter_name' name is in the allowed list and the related
	 * Imagick constant is defined or fall back to the default filter.
	 */
	if ( in_array( $filter_name, $allowed_filters, true ) && defined( 'Imagick::' . $filter_name ) ) {
		$filter = constant( 'Imagick::' . $filter_name );
	} else {
		$filter = defined( 'Imagick::FILTER_TRIANGLE' ) ? Imagick::FILTER_TRIANGLE : false;
	}

	/**
	 * Filters whether to strip metadata from images when they're resized.
	 *
	 * This filter only applies when resizing using the Imagick editor since GD
	 * always strips profiles by default.
	 *
	 * @since 4.5.0
	 *
	 * @param bool $strip_meta Whether to strip image metadata during resizing. Default true.
	 */
	if ( apply_filters( 'image_strip_meta', $strip_meta ) ) {
		$this->strip_meta(); // Fail silently if not supported.
	}

	try {
		/**
		 * Special handling for certain types of PNG images:
		 * 1. For PNG images, we need to specify compression settings and remove unneeded chunks.
		 * 2. For indexed PNG images, the number of colors must not exceed 256.
		 * 3. For indexed PNG images with an alpha channel, the tRNS chunk must be preserved.
		 * 4. For indexed PNG images with true alpha transparency (an alpha channel > 1 bit), we need to avoid saving
		 * the image using ImageMagick's 'png8' format,  because that supports only binary (1 bit) transparency.
		 *
		 * For #4 we want to check whether the image has a 1-bit alpha channel before resizing,  because resizing
		 * may cause the number of alpha values to multiply due to antialiasing. If the original image had only a
		 * 1-bit alpha channel, then a 1-bit alpha channel should be good enough for the resized images.
		 *
		 * Perform all the necessary checks before resizing the image and store the results in variables for later use.
		 */
		$is_png                                      = false;
		$is_indexed_png                              = false;
		$is_indexed_png_with_alpha_channel           = false;
		$is_indexed_png_with_true_alpha_transparency = false;

		if ( 'image/png' === $this->mime_type ) {
			$is_png = true;

			if (
				is_callable( array( $this->image, 'getImageProperty' ) )
				&& '3' === $this->image->getImageProperty( 'png:IHDR.color-type-orig' )
			) {
				$is_indexed_png = true;

				if (
					is_callable( array( $this->image, 'getImageAlphaChannel' ) )
					&& $this->image->getImageAlphaChannel()
				) {
					$is_indexed_png_with_alpha_channel = true;

					if (
						is_callable( array( $this->image, 'getImageChannelDepth' ) )
						&& defined( 'Imagick::CHANNEL_ALPHA' )
						&& 1 < $this->image->getImageChannelDepth( Imagick::CHANNEL_ALPHA )
					) {
						$is_indexed_png_with_true_alpha_transparency = true;
					}
				}
			}
		}

		/*
		 * To be more efficient, resample large images to 5x the destination size before resizing
		 * whenever the output size is less that 1/3 of the original image size (1/3^2 ~= .111),
		 * unless we would be resampling to a scale smaller than 128x128.
		 */
		if ( is_callable( array( $this->image, 'sampleImage' ) ) ) {
			$resize_ratio  = ( $dst_w / $this->size['width'] ) * ( $dst_h / $this->size['height'] );
			$sample_factor = 5;

			if ( $resize_ratio < .111 && ( $dst_w * $sample_factor > 128 && $dst_h * $sample_factor > 128 ) ) {
				$this->image->sampleImage( $dst_w * $sample_factor, $dst_h * $sample_factor );
			}
		}

		/*
		 * Use resizeImage() when it's available and a valid filter value is set.
		 * Otherwise, fall back to the scaleImage() method for resizing, which
		 * results in better image quality over resizeImage() with default filter
		 * settings and retains backward compatibility with pre 4.5 functionality.
		 */
		if ( is_callable( array( $this->image, 'resizeImage' ) ) && $filter ) {
			$this->image->setOption( 'filter:support', '2.0' );
			$this->image->resizeImage( $dst_w, $dst_h, $filter, 1 );
		} else {
			$this->image->scaleImage( $dst_w, $dst_h );
		}

		// Set appropriate quality settings after resizing.
		if ( 'image/jpeg' === $this->mime_type ) {
			if ( is_callable( array( $this->image, 'unsharpMaskImage' ) ) ) {
				$this->image->unsharpMaskImage( 0.25, 0.25, 8, 0.065 );
			}

			$this->image->setOption( 'jpeg:fancy-upsampling', 'off' );
		}

		if ( $is_png ) {
			$this->image->setOption( 'png:compression-filter', '5' );
			$this->image->setOption( 'png:compression-level', '9' );
			$this->image->setOption( 'png:compression-strategy', '1' );

			// Indexed PNG files get some additional handling.
			// See #63448 for details.
			if ( $is_indexed_png ) {

				// Check for an alpha channel.
				if ( $is_indexed_png_with_alpha_channel ) {
					$this->image->setOption( 'png:include-chunk', 'tRNS' );
				} else {
					$this->image->setOption( 'png:exclude-chunk', 'all' );
				}

				$this->image->quantizeImage( 256, $this->image->getColorspace(), 0, false, false );

				/*
				 * If the colorspace is 'gray', use the png8 format to ensure it stays indexed.
				 * ImageMagick tends to save grayscale images as grayscale PNGs rather than indexed PNGs,
				 * even though grayscale PNGs usually have considerably larger file sizes.
				 * But we can force ImageMagick to save the image as an indexed PNG instead,
				 * by telling it to use png8 format.
				 *
				 * Note that we need to first call quantizeImage() before checking getImageColorspace(),
				 * because only after calling quantizeImage() will the colorspace be COLORSPACE_GRAY for grayscale images
				 * (and we have not found any other way to identify grayscale images).
				 *
				 * We need to avoid forcing indexed format for images with true alpha transparency,
				 * because ImageMagick does not support saving an image with true alpha transparency as an indexed PNG.
				 */
				if ( Imagick::COLORSPACE_GRAY === $this->image->getImageColorspace() && ! $is_indexed_png_with_true_alpha_transparency ) {
					// Set the image format to Indexed PNG.
					$this->image->setOption( 'png:format', 'png8' );
				}
			} else {
				$this->image->setOption( 'png:exclude-chunk', 'all' );
			}
		}

		/*
		 * If alpha channel is not defined, set it opaque.
		 *
		 * Note that Imagick::getImageAlphaChannel() is only available if Imagick
		 * has been compiled against ImageMagick version 6.4.0 or newer.
		 */
		if ( is_callable( array( $this->image, 'getImageAlphaChannel' ) )
			&& is_callable( array( $this->image, 'setImageAlphaChannel' ) )
			&& defined( 'Imagick::ALPHACHANNEL_UNDEFINED' )
			&& defined( 'Imagick::ALPHACHANNEL_OPAQUE' )
		) {
			if ( $this->image->getImageAlphaChannel() === Imagick::ALPHACHANNEL_UNDEFINED ) {
				$this->image->setImageAlphaChannel( Imagick::ALPHACHANNEL_OPAQUE );
			}
		}

		// Limit the bit depth of resized images.
		if ( is_callable( array( $this->image, 'getImageDepth' ) ) && is_callable( array( $this->image, 'setImageDepth' ) ) ) {
			/**
			 * Filters the maximum bit depth of resized images.
			 *
			 * This filter only applies when resizing using the Imagick editor since GD
			 * does not support getting or setting bit depth.
			 *
			 * Use this to adjust the maximum bit depth of resized images.
			 *
			 * @since 6.8.0
			 *
			 * @param int $max_depth   The maximum bit depth. Default is the input depth.
			 * @param int $image_depth The bit depth of the original image.
			 */
			$max_depth = apply_filters( 'image_max_bit_depth', $this->image->getImageDepth(), $this->image->getImageDepth() );
			$this->image->setImageDepth( $max_depth );
		}
	} catch ( Exception $e ) {
		return new WP_Error( 'image_resize_error', $e->getMessage() );
	}
}