Skip to content
50 changes: 39 additions & 11 deletions src/wp-admin/includes/image.php
Original file line number Diff line number Diff line change
Expand Up @@ -506,13 +506,20 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $mim
return array();
}

$original_mime_type = wp_get_image_mime( $file );

if ( ! $mime_type ) {
$mime_type = wp_get_image_mime( $file );
$mime_type = $original_mime_type;
}

// Check if any of the new sizes already exist.
if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) {
foreach ( $image_meta['sizes'] as $size_name => $size_meta ) {
// Check if the output mime is enabled for this image size.
if ( ! _wp_mime_type_available_for_image_size( $attachment_id, $size_name, $original_mime_type, $mime_type ) ) {
continue;
}

/*
* Only checks "size name" so we don't override existing images even if the dimensions
* don't match the currently defined size with the same name.
Expand Down Expand Up @@ -606,6 +613,21 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $mim
return $image_meta;
}

/**
* Check whether mime type output is available for a given size and mime type.
*
* @since 6.1.0
*
* @param int $attachment_id Attachment ID.
* @param string $size_name Size name to check.
* @return bool Whether the size is available for the given mime type.
*/
function _wp_mime_type_available_for_image_size( $attachment_id, $size_name, $source_mime, $destination_mime ) {
$image_mime_transforms = wp_upload_image_mime_transforms( $attachment_id, $size_name );

return isset( $image_mime_transforms[ $source_mime ] [ $destination_mime ] );
}

/**
* Low-level function to create full-size images in additional mime types.
*
Expand Down Expand Up @@ -1338,28 +1360,34 @@ function _copy_image_file( $attachment_id ) {
* For example an `image/jpeg` should be converted into an `image/jpeg` and `image/webp`. The first type
* is considered the primary output type for this image.
*
* Called for each uploaded image to determine the list of mime types that should be converted into. Then,
* called again for each target image size as they are generated to see if the image should be converted into the mime type
* for that size.
*
* @since 6.1.0
*
* @param $attachment_id int The attachment ID.
* @return array An array of valid mime types, where the key is the source file mime type and the list of mime types to
* generate.
* @param int $attachment_id The attachment ID.
* @param string $image_size The image size name. False when called on the image and target size is unavailable.
* @return array An array of valid mime types, where the key is the source file mime type and the value is the list of
* mime types to generate.
*/
function wp_upload_image_mime_transforms( $attachment_id ) {
function wp_upload_image_mime_transforms( $attachment_id, $image_size ) {
$image_mime_transforms = array(
'image/jpeg' => array( 'image/jpeg', 'image/webp' ),
'image/webp' => array( 'image/webp', 'image/jpeg' ),
);

/**
* Filter to the output mime types for a given input mime type.
* Filter to the output mime types for a given input mime type and image size.
*
* @since 6.1.0
*
* @param array $image_mime_transforms A map with the valid mime transforms where the key is the source file mime type
* and the value is one or more mime file types to generate.
* @param int $attachment_id The ID of the attachment where the hook was dispatched.
* @param array $image_mime_transforms A map with the valid mime transforms where the key is the source file mime type
* and the value is one or more mime file types to generate.
* @param int $attachment_id The ID of the attachment where the hook was dispatched.
* @param string $image_size The image size name. Optional.
*/
return (array) apply_filters( 'wp_upload_image_mime_transforms', $image_mime_transforms, $attachment_id );
return apply_filters( 'wp_upload_image_mime_transforms', $image_mime_transforms, $attachment_id, $image_size );
}

/**
Expand All @@ -1373,7 +1401,7 @@ function wp_upload_image_mime_transforms( $attachment_id ) {
* @return array An array with two entries, the primary mime type and the list of additional mime types.
*/
function _wp_get_primary_and_additional_mime_types( $file, $attachment_id ) {
$image_mime_transforms = wp_upload_image_mime_transforms( $attachment_id );
$image_mime_transforms = wp_upload_image_mime_transforms( $attachment_id, false );
$original_mime_type = wp_get_image_mime( $file );
$output_mime_types = isset( $image_mime_transforms[ $original_mime_type ] ) ? $image_mime_transforms[ $original_mime_type ] : array( $original_mime_type );

Expand Down
52 changes: 45 additions & 7 deletions tests/phpunit/tests/image/editor.php
Original file line number Diff line number Diff line change
Expand Up @@ -403,10 +403,10 @@ public function provider_image_with_default_behaviors_during_upload() {
/**
* Not create the sources property if no transform is provided
*
* @since 6.0.0
* @since 6.1.0
*/
public function it_should_not_create_the_sources_property_if_no_transform_is_provided() {
add_filter( 'webp_uploads_supported_image_mime_transforms', '__return_empty_array' );
add_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' );

$attachment_id = $this->factory->attachment->create_upload_object(
DIR_TESTDATA . '/images/test-image.jpg'
Expand All @@ -423,11 +423,11 @@ public function it_should_not_create_the_sources_property_if_no_transform_is_pro
/**
* Create the sources property when no transform is available
*
* @since 6.0.0
* @since 6.1.0
*/
public function it_should_create_the_sources_property_when_no_transform_is_available() {
add_filter(
'webp_uploads_supported_image_mime_transforms',
'wp_upload_image_mime_transforms',
function () {
return array( 'image/jpeg' => array() );
}
Expand All @@ -453,11 +453,11 @@ function () {
/**
* Not create the sources property if the mime is not specified on the transforms images
*
* @since 6.0.0
* @since 6.1.0
*/
public function it_should_not_create_the_sources_property_if_the_mime_is_not_specified_on_the_transforms_images() {
add_filter(
'webp_uploads_supported_image_mime_transforms',
'wp_upload_image_mime_transforms',
function () {
return array( 'image/jpeg' => array() );
}
Expand All @@ -475,6 +475,44 @@ function () {
}
}

/**
* Do not create the sources property when opting out by size.
* @since 6.1.0
*/
public function it_should_not_create_the_sources_property_when_opting_out_by_size() {
// Exclude the large image from mime transforms.
add_filter(
'wp_upload_image_mime_transforms',
function ( $transforms, $attachment_id, $size_name ) {
if ( 'large' === $size_name ) {
return array();
}
return $transforms;
},
10,
3
);

$attachment_id = $this->factory->attachment->create_upload_object(
DIR_TESTDATA . '/images/test-image.jpg'
);

$metadata = wp_get_attachment_metadata( $attachment_id );

$this->assertIsArray( $metadata );
// Expect sources data for every size except the large size.
foreach ( $metadata['sizes'] as $size_name => $properties ) {
if ( 'large' === $size_name ) {
$this->assertArrayNotHasKey( 'sources', $properties );
} else {
$this->assertArrayHasKey( 'sources', $properties );

}
}
}



/**
* Create a WebP version with all the required properties
*
Expand Down Expand Up @@ -736,7 +774,7 @@ public function it_should_contain_the_full_image_size_from_the_original_mime() {
* @since 6.0.0
*/
public function it_should_prevent_replacing_an_image_with_no_available_sources() {
add_filter( 'webp_uploads_supported_image_mime_transforms', '__return_empty_array' );
add_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' );

$attachment_id = $this->factory->attachment->create_upload_object( DIR_TESTDATA . '/images/test-image.jpg' );

Expand Down