Skip to content

Commit 6164fbe

Browse files
authored
Merge pull request #418 from WordPress/fix/372-discard-larger-webp
Discard WebP image if it is larger than corresponding JPEG image
2 parents 3171285 + 5a6694c commit 6164fbe

6 files changed

Lines changed: 271 additions & 118 deletions

File tree

‎modules/images/webp-uploads/helper.php‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,35 @@ function webp_uploads_get_attachment_sources( $attachment_id, $size = 'thumbnail
240240
// Return an empty array if no sources found.
241241
return array();
242242
}
243+
244+
/**
245+
* Check whether the additional image is larger than the original image.
246+
*
247+
* @since n.e.x.t
248+
*
249+
* @param array $original An array with the metadata of the attachment.
250+
* @param array $additional An array containing the filename and file size for additional mime.
251+
* @return bool True if the additional image is larger than the original image, otherwise false.
252+
*/
253+
function webp_uploads_should_discard_additional_image_file( array $original, array $additional ) {
254+
$original_image_filesize = isset( $original['filesize'] ) ? (int) $original['filesize'] : 0;
255+
$additional_image_filesize = isset( $additional['filesize'] ) ? (int) $additional['filesize'] : 0;
256+
if ( $original_image_filesize > 0 && $additional_image_filesize > 0 ) {
257+
/**
258+
* Filter whether WebP images that are larger than the matching JPEG should be discarded.
259+
*
260+
* By default the performance lab plugin will use the mime type with the smaller filesize
261+
* rather than defaulting to `webp`.
262+
*
263+
* @since n.e.x.t
264+
*
265+
* @param bool $preferred_filesize Prioritize file size over mime type. Default true.
266+
*/
267+
$webp_discard_larger_images = apply_filters( 'webp_uploads_discard_larger_generated_images', true );
268+
269+
if ( $webp_discard_larger_images && $additional_image_filesize >= $original_image_filesize ) {
270+
return true;
271+
}
272+
}
273+
return false;
274+
}

‎modules/images/webp-uploads/load.php‎

Lines changed: 77 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ function webp_uploads_create_sources_property( array $metadata, $attachment_id )
9696
continue;
9797
}
9898

99+
if ( webp_uploads_should_discard_additional_image_file( $metadata, $image ) ) {
100+
wp_delete_file_from_directory( $destination, $original_directory );
101+
continue;
102+
}
103+
99104
$metadata['sources'][ $targeted_mime ] = $image;
100105
wp_update_attachment_metadata( $attachment_id, $metadata );
101106
}
@@ -156,6 +161,12 @@ function webp_uploads_create_sources_property( array $metadata, $attachment_id )
156161
continue;
157162
}
158163

164+
if ( webp_uploads_should_discard_additional_image_file( $properties, $source ) ) {
165+
$destination = path_join( $original_directory, $source['file'] );
166+
wp_delete_file_from_directory( $destination, $original_directory );
167+
continue;
168+
}
169+
159170
$properties['sources'][ $mime ] = $source;
160171
$metadata['sizes'][ $size_name ] = $properties;
161172
wp_update_attachment_metadata( $attachment_id, $metadata );
@@ -481,6 +492,7 @@ function webp_uploads_update_image_references( $content ) {
481492
* for the specified image sizes, the *.webp references are stored inside of each size.
482493
*
483494
* @since 1.0.0
495+
* @since n.e.x.t Remove `webp_uploads_prefer_smaller_image_file` filter
484496
*
485497
* @param string $original_image An <img> tag where the urls would be updated.
486498
* @param string $context The context where this is function is being used.
@@ -490,24 +502,11 @@ function webp_uploads_update_image_references( $content ) {
490502
function webp_uploads_img_tag_update_mime_type( $original_image, $context, $attachment_id ) {
491503
$image = $original_image;
492504
$metadata = wp_get_attachment_metadata( $attachment_id );
505+
493506
if ( empty( $metadata['file'] ) ) {
494507
return $image;
495508
}
496509

497-
/**
498-
* Filters whether the smaller image should be used regardless of which MIME type is preferred overall.
499-
*
500-
* This is disabled by default only because it is not part of the current WordPress core feature proposal.
501-
*
502-
* By enabling this, the plugin will compare the image file sizes and prefer the smaller file regardless of MIME
503-
* type.
504-
*
505-
* @since 1.0.0
506-
*
507-
* @param bool $prefer_smaller_image_file Whether to prefer the smaller image file.
508-
*/
509-
$prefer_smaller_image_file = apply_filters( 'webp_uploads_prefer_smaller_image_file', false );
510-
511510
/**
512511
* Filters mime types that should be used to update all images in the content. The order of
513512
* mime types matters. The first mime type in the list will be used if it is supported by an image.
@@ -523,58 +522,73 @@ function webp_uploads_img_tag_update_mime_type( $original_image, $context, $atta
523522
// Get the original mime type for comparison.
524523
$original_mime = get_post_mime_type( $attachment_id );
525524

526-
$target_mime = null;
527-
foreach ( $target_mimes as $mime ) {
528-
if ( isset( $metadata['sources'][ $mime ] ) ) {
529-
$target_mime = $mime;
530-
break;
525+
foreach ( $target_mimes as $target_mime ) {
526+
527+
if ( $target_mime === $original_mime ) {
528+
continue;
531529
}
532-
}
533530

534-
if ( null === $target_mime ) {
535-
return $image;
536-
}
531+
if ( ! isset( $metadata['sources'][ $target_mime ]['file'] ) ) {
532+
continue;
533+
}
537534

538-
// Replace the full size image if present.
539-
if ( isset( $metadata['sources'][ $target_mime ]['file'] ) ) {
540-
// Initially set the target mime as the replacement source.
541-
$replacement_source = $metadata['sources'][ $target_mime ]['file'];
535+
/**
536+
* Filter to replace additional image source file, by locating the original
537+
* mime types of the file and return correct file path in the end.
538+
*
539+
* Altering the $image tag through this filter effectively short-circuits the default replacement logic using the preferred MIME type.
540+
*
541+
* @since 1.1.0
542+
*
543+
* @param string $image An <img> tag where the urls would be updated.
544+
* @param int $attachment_id The ID of the attachment being modified.
545+
* @param string $size The size name that would be used to create this image, out of the registered subsizes.
546+
* @param string $target_mime The target mime in which the image should be created.
547+
* @param string $context The context where this is function is being used.
548+
*/
549+
$filtered_image = (string) apply_filters( 'webp_uploads_pre_replace_additional_image_source', $image, $attachment_id, 'full', $target_mime, $context );
542550

543-
// Check for the smaller image file.
544-
if (
545-
$prefer_smaller_image_file &&
546-
! empty( $metadata['sources'][ $target_mime ]['filesize'] ) &&
547-
! empty( $metadata['sources'][ $original_mime ]['filesize'] ) &&
548-
$metadata['sources'][ $original_mime ]['filesize'] < $metadata['sources'][ $target_mime ]['filesize']
549-
) {
550-
// Set the original source file as the replacement if smaller.
551-
$replacement_source = $metadata['sources'][ $original_mime ]['file'];
551+
// If filtered image is same as the image, run our own replacement logic, otherwise rely on the filtered image.
552+
if ( $filtered_image === $image ) {
553+
$basename = wp_basename( $metadata['file'] );
554+
$image = str_replace(
555+
$basename,
556+
$metadata['sources'][ $target_mime ]['file'],
557+
$image
558+
);
559+
} else {
560+
$image = $filtered_image;
561+
}
562+
}
563+
564+
// Replace sub sizes for the image if present.
565+
foreach ( $metadata['sizes'] as $size => $size_data ) {
566+
if ( empty( $size_data['file'] ) ) {
567+
continue;
552568
}
553569

554-
$basename = wp_basename( $metadata['file'] );
555-
if ( $basename !== $replacement_source ) {
570+
foreach ( $target_mimes as $target_mime ) {
556571

557-
/**
558-
* Filter to replace additional image source file, by locating the original
559-
* mime types of the file and return correct file path in the end.
560-
*
561-
* Altering the $image tag through this filter effectively short-circuits the default replacement logic using the preferred MIME type.
562-
*
563-
* @since 1.1.0
564-
*
565-
* @param string $image An <img> tag where the urls would be updated.
566-
* @param int $attachment_id The ID of the attachment being modified.
567-
* @param string $size The size name that would be used to create this image, out of the registered subsizes.
568-
* @param string $target_mime The target mime in which the image should be created.
569-
* @param string $context The context where this is function is being used.
570-
*/
571-
$filtered_image = (string) apply_filters( 'webp_uploads_pre_replace_additional_image_source', $image, $attachment_id, 'full', $target_mime, $context );
572+
if ( $target_mime === $original_mime ) {
573+
continue;
574+
}
575+
576+
if ( ! isset( $size_data['sources'][ $target_mime ]['file'] ) ) {
577+
continue;
578+
}
579+
580+
if ( $size_data['file'] === $size_data['sources'][ $target_mime ]['file'] ) {
581+
continue;
582+
}
583+
584+
/** This filter is documented in modules/images/webp-uploads/load.php */
585+
$filtered_image = (string) apply_filters( 'webp_uploads_pre_replace_additional_image_source', $image, $attachment_id, $size, $target_mime, $context );
572586

573587
// If filtered image is same as the image, run our own replacement logic, otherwise rely on the filtered image.
574588
if ( $filtered_image === $image ) {
575589
$image = str_replace(
576-
$basename,
577-
$metadata['sources'][ $target_mime ]['file'],
590+
$size_data['file'],
591+
$size_data['sources'][ $target_mime ]['file'],
578592
$image
579593
);
580594
} else {
@@ -583,53 +597,21 @@ function webp_uploads_img_tag_update_mime_type( $original_image, $context, $atta
583597
}
584598
}
585599

586-
// Replace sub sizes for the image if present.
587-
foreach ( $metadata['sizes'] as $size => $size_data ) {
588-
if ( empty( $size_data['file'] ) ) {
589-
continue;
590-
}
591-
592-
if ( empty( $size_data['sources'][ $target_mime ]['file'] ) ) {
593-
continue;
594-
}
600+
foreach ( $target_mimes as $target_mime ) {
595601

596-
if ( $size_data['file'] === $size_data['sources'][ $target_mime ]['file'] ) {
602+
if ( $target_mime === $original_mime ) {
597603
continue;
598604
}
599605

600-
// Do not update image URL if the target image is larger than the original.
601606
if (
602-
$prefer_smaller_image_file &&
603-
! empty( $size_data['sources'][ $target_mime ]['filesize'] ) &&
604-
! empty( $size_data['sources'][ $original_mime ]['filesize'] ) &&
605-
$size_data['sources'][ $original_mime ]['filesize'] < $size_data['sources'][ $target_mime ]['filesize']
607+
! has_action( 'wp_footer', 'webp_uploads_wepb_fallback' ) &&
608+
$image !== $original_image &&
609+
'the_content' === $context &&
610+
'image/jpeg' === $original_mime &&
611+
'image/webp' === $target_mime
606612
) {
607-
continue;
613+
add_action( 'wp_footer', 'webp_uploads_wepb_fallback' );
608614
}
609-
610-
/** This filter is documented in modules/images/webp-uploads/load.php */
611-
$filtered_image = (string) apply_filters( 'webp_uploads_pre_replace_additional_image_source', $image, $attachment_id, $size, $target_mime, $context );
612-
613-
// If filtered image is same as the image, run our own replacement logic, otherwise rely on the filtered image.
614-
if ( $filtered_image === $image ) {
615-
$image = str_replace(
616-
$size_data['file'],
617-
$size_data['sources'][ $target_mime ]['file'],
618-
$image
619-
);
620-
} else {
621-
$image = $filtered_image;
622-
}
623-
}
624-
625-
if (
626-
! has_action( 'wp_footer', 'webp_uploads_wepb_fallback' ) &&
627-
$image !== $original_image &&
628-
'the_content' === $context &&
629-
'image/jpeg' === $original_mime &&
630-
'image/webp' === $target_mime
631-
) {
632-
add_action( 'wp_footer', 'webp_uploads_wepb_fallback' );
633615
}
634616

635617
return $image;

‎tests/modules/images/webp-uploads/helper-tests.php‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,4 +410,48 @@ function () {
410410
$this->assertIsArray( $transforms );
411411
$this->assertSame( array( 'image/jpeg' => array( 'image/jpeg', 'image/webp' ) ), $transforms );
412412
}
413+
414+
/**
415+
* @dataProvider data_provider_image_filesize
416+
*
417+
* @test
418+
*/
419+
public function it_should_discard_additional_image_if_larger_than_the_original_image( $original_filesize, $additional_filesize, $expected_status ) {
420+
add_filter( 'webp_uploads_discard_larger_generated_images', '__return_true' );
421+
422+
$output = webp_uploads_should_discard_additional_image_file( $original_filesize, $additional_filesize );
423+
$this->assertSame( $output, $expected_status );
424+
}
425+
426+
public function data_provider_image_filesize() {
427+
return array(
428+
array(
429+
array( 'filesize' => 120101 ),
430+
array( 'filesize' => 100101 ),
431+
false,
432+
),
433+
array(
434+
array( 'filesize' => 100101 ),
435+
array( 'filesize' => 120101 ),
436+
true,
437+
),
438+
array(
439+
array( 'filesize' => 10101 ),
440+
array( 'filesize' => 10101 ),
441+
true,
442+
),
443+
);
444+
}
445+
446+
/**
447+
* @dataProvider data_provider_image_filesize
448+
*
449+
* @test
450+
*/
451+
public function it_should_never_discard_additional_image_if_filter_is_false( $original_filesize, $additional_filesize ) {
452+
add_filter( 'webp_uploads_discard_larger_generated_images', '__return_false' );
453+
454+
$output = webp_uploads_should_discard_additional_image_file( $original_filesize, $additional_filesize );
455+
$this->assertFalse( $output );
456+
}
413457
}

‎tests/modules/images/webp-uploads/image-edit-tests.php‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010

1111
class WebP_Uploads_Image_Edit_Tests extends ImagesTestCase {
1212

13+
public function set_up() {
14+
parent::set_up();
15+
16+
add_filter( 'webp_uploads_discard_larger_generated_images', '__return_false' );
17+
}
18+
1319
/**
1420
* Backup the sources structure alongside the full size
1521
*

0 commit comments

Comments
 (0)