Make WordPress Core

Opened 2 years ago

Last modified 6 months ago

#54648 accepted enhancement

Improve filter to enable setting quality for each subsize image

Reported by: mte90's profile Mte90 Owned by: adamsilverstein's profile adamsilverstein
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Media Keywords: has-patch needs-unit-tests
Focuses: performance Cc:

Description

Talking with https://profiles.wordpress.org/codekraft/ we was wondering if it is possible to get a filter to specify the quality of subsized images.

Right now the wp_update_image_subsizes https://github.com/WordPress/wordpress-develop/blob/cdd5e43a04930bac390537944eb6f4256d364bd4/src/wp-admin/includes/image.php#L153 doesn't have that filter.

So on wp_create_image_subsizes https://github.com/WordPress/wordpress-develop/blob/cdd5e43a04930bac390537944eb6f4256d364bd4/src/wp-admin/includes/image.php#L228 calls _wp_make_subsizes https://github.com/WordPress/wordpress-develop/blob/cdd5e43a04930bac390537944eb6f4256d364bd4/src/wp-admin/includes/image.php#L386

But doesn't specify the quality.
Probably the best solution is to use set_quality of the Image Editor ImageMagick/GD classes in the last function.

This can help, in our thoughts, on generate smallest images (like under 200x200) with a different compression that can be more strong, compared to the others, as they have a different usage and the quality of those can be different.
This can save space and speed up the page.

Some examples? In the woocommerce gallery where there are the preview as tiny squares (150x150).

Maybe this idea can interest the new Performance team?

Change History (23)

#1 @codekraft
2 years ago

I think it's interesting and curious how I would have used completely the opposite example (even if MTE90's is however valid) as proof that this ticket would be really helpful...

My example are hdpi images that can be compressed to insane levels without visibly losing quality, these are generally all the images between >2000px and fullsize, the ones that can make the uploads folder grow bigger... A jpg hdpi image compressed to 19% quality requires the same space as a mdpi image compressed to 51% quality without loss of quality (but with twice the resolution). This happens because even if there are more artifacts due to higher compression these are "hidden" by the highter resolution. source: https://alidark.com/responsive-retina-image-mobile/

example:
MDPI 50kb 640x427 - quality 85%:
https://i.imgur.com/MOUyOqd.jpg
HDPI 50kb 1280x854 - quality 35%:
https://i.imgur.com/VS5VD9p.jpg

The same HDPI image at 85% quality - 1280x854 sizes >150kb and the possibility to choose the quality would allow to gain 300% of the download speed for the (almost) same image

But of course I think that also compressing just a little bit the small thumb images is a good idea, in this way they are not "degraded" by compression since at that scale isn't really relevant the difference in kb


In the future handling images quality will be useful also for webp/avif as well as jpg

Last edited 2 years ago by codekraft (previous) (diff)

#2 @adamsilverstein
2 years ago

Hit @Mte90 - thanks for the bug report / feature request.

Are you saying the wp_editor_set_quality filter (https://developer.wordpress.org/reference/hooks/wp_editor_set_quality/) is not applied during image sub-size generation?

This filter is in src/wp-includes/class-wp-image-editor.php and I would have expected it to be applied because set_quality is called by each image editor during load.

We still need to validate this filter works correctly when changing formats on upload (eg converting jpeg->webp).

Currently that filter passes the image mime type, I wonder if there is more context data we could pass here (like image size) to address your idea of different compression levels?

#3 @Mte90
2 years ago

The real issue is that is not possible to understand as it is now when the resize is for the full image or for a subsize image and what is that size.
If the filter can add this information it is possible to do more validation about the image itself if it is the case to compress or not.

#4 @adamsilverstein
2 years ago

  • Keywords needs-patch added
  • Owner set to adamsilverstein
  • Status changed from new to accepted
  • Summary changed from Filter to let define the quality on subsize images to Improve filter to enable setting quality for each subsize image

The real issue is that is not possible to understand as it is now when the resize is for the full image or for a subsize image and what is that size.

Ok, got it - so if the filter passed additional context such as a reference to the image ID or the size meta, you would have what you need to set the appropriate compression level. (or perhaps we need a different filter)... Great suggestion, this feels like a nice enhancement!

I modified the ticket title to clarify the request is about setting quality for each sub sized image.

This ticket was mentioned in PR #2189 on WordPress/wordpress-develop by adamsilverstein.


23 months ago
#5

  • Keywords has-patch added; needs-patch removed

#6 @adamsilverstein
23 months ago

  • Keywords needs-unit-tests added

#7 @adamsilverstein
23 months ago

@Mte90 Would something like https://github.com/WordPress/wordpress-develop/pull/2189 work? I haven't tested at all yet and we need to add some tests.

#8 @Mte90
23 months ago

I think that apart the size in pixel it can be handy also the size name, like thumbnail and so on in this way it is not required to do another check for all of them what is (performance again).

As example if the size is 100x100 how I know what is the size name? Maybe I want to apply it just for thumbnail and I don't care of the size in pixel at all.

Anyway I think that we are in the right track.

#9 follow-up: @adamsilverstein
23 months ago

As example if the size is 100x100 how I know what is the size name? Maybe I want to apply it just for thumbnail and I don't care of the size in pixel at all.

I did consider this, however at the point where we apply the wp_editor_set_quality filter, we don't have the size "name" - only the dimensions.

In addition, it is possible that more than one size "name" matches the size dimensions, so we can't really guess for you. This article includes a code snippet that would let you look up the name if you need that: https://wordpress.stackexchange.com/questions/243446/get-images-size-name-from-its-dimension

#10 in reply to: ↑ 9 @Mte90
23 months ago

Replying to adamsilverstein:

As example if the size is 100x100 how I know what is the size name? Maybe I want to apply it just for thumbnail and I don't care of the size in pixel at all.

I did consider this, however at the point where we apply the wp_editor_set_quality filter, we don't have the size "name" - only the dimensions.

Well at that point I think that is the best we can :-)

To me is a go!

#11 @Mte90
23 months ago

Talking with @codekraft seems that this filter is not executed for any size but just once.

With this code seems that it isn't executed:

<?php
add_filter('wp_editor_set_quality', function ($quality, $mime_type, $size) {
    return ($mime_type == 'image/jpeg' && max($size) > 2000) ? round($quality * .1) : $quality;
}, 10, 3);

Documentation says Applies only during initial editor instantiation, or when set_quality() is run manually without the $quality argument. I am not expert of this component, so it is clear if for initial means for any subsize or just once for the original image.

#12 @adamsilverstein
23 months ago

Talking with @codekraft seems that this filter is not executed for any size but just once.

Hmmm, yea that might be the case, let me add some tests and we can verify.

With this code seems that it isn't executed:

Let me give this a test. You mean it was only executed once (and then the same quality used for all images?) Where did you add that code?

#13 @codekraft
23 months ago

hi @adamsilverstein, it seems so, the same quality is used for all images. I added that snippet at the end of functions.php in my template.

To make a quick test just use that snippet and upload an image above 2000px, what I expect is that the images above 2000px are compressed but it seems that a are applied to all.

This ticket was mentioned in Slack in #core-media by adamsilverstein. View the logs.


23 months ago

#15 follow-up: @birgire
17 months ago

I'm not up to date on this, but I just recalled playing with this many years ago (2014) here

https://wordpress.stackexchange.com/questions/163844/create-image-formats-with-different-qualities-when-uploading/165241#165241

to modify the image quality before the image size is generated.

Let me just quote my comment there in case it might still apply here:

https://wordpress.stackexchange.com/questions/163844/create-image-formats-with-different-qualities-when-uploading/165241#comment240183_165241

I almost managed to solve this with only filters (intermediate_image_sizes_advanced,
image_resize_dimensions and jpeg_quality filter) within a custom class, but was stopped by single
line in the get_quality() method of the WP_Image_Editor class. It's the check: if ( ! $this->quality ),
that's made before the jpeg_quality filter is activated. That means you can only use that filter
once, for all intermediate jpeg image sizes. So if we want to modify the quality before the files are
saved, then I don't see another way then to extend the class as I did in my answer ;-)

The jpeg_quality has since been deprecated for wp_editor_set_quality.

I might be refering there to this line here:

https://github.com/WordPress/wordpress-develop/blob/a14324ca3af4eeff69406d3e59719a33f055f07e/src/wp-includes/class-wp-image-editor.php#L224

in this method:

        public function get_quality() {
		if ( ! $this->quality ) {
			$this->set_quality();
		}

		return $this->quality;
	}

#16 in reply to: ↑ 15 @codekraft
17 months ago

hi @birgire,
i've also found a way to extend the WordPress image generation capabilities, I use "wp_generate_attachment_metadata" to hook after the standard generation process. some examples here: webp or square images.

However I think it is a really impractical way and I would like more granular control over image generation, currently (width, height, crop) is really tight for today's needs

it will also be convenient in the future because some new formats have the quality parameter that works completely different from jpg, for example the default quality of avif is 50 (out of 100) when for jpg it was 87

#17 @birgire
17 months ago

Thanks for links @codekraft

Yes I agree, the main reason for my comment was to point out a possible limitation on the current quality filter (because of the line that checks if the quality is unmodified) that might not be obvious from the docs when testing it out.

Probably another filter would be needed here to avoid the current limitations and allow it to target different intermediate sizes and formats. Also to avoid changing current behavior of the existing filter.

Last edited 17 months ago by birgire (previous) (diff)

This ticket was mentioned in Slack in #core-media by adamsilverstein. View the logs.


17 months ago

This ticket was mentioned in Slack in #core-media by adamsilverstein. View the logs.


17 months ago

#20 @adamsilverstein
17 months ago

Once idea to consider here instead of or in addition to the filter improvements would be adding a quality setting directly to add_image_size (see #56288). This would enable setting a "default" quality for each size (which could then still be filtered at run time.

Yes I agree, the main reason for my comment was to point out a possible limitation on the current quality filter (because of the line that checks if the quality is unmodified) that might not be obvious from the docs when testing it out.

@birgire you are right, the current filter is not sufficient because it isn't run for each size, but rather for the image as a whole, then the same quality is used for all sizes. To enable the feature requested in this ticket, I believe the filter would need to run _for each size_ passing the size name as context.

Probably another filter would be needed here to avoid the current limitations and allow it to target different intermediate sizes and formats. Also to avoid changing current behavior of the existing filter.

Possibly a new filter, or possibly we could use the same filter - running it once for each size with the size name passed as a new context variable. Existing code would continue to function, however the filter would run multiple times instead of once. I'm not sure which approach would be better, I would look to existing filter patterns in core for precedent.

#21 @birgire
16 months ago

@adamsilverstein having the possibility to set the quality per size with add_image_size(), via an option array, as you mentioned in the #56288 ticket, sounds like an interesting approach. At first thought this seems like a natural way to be able to config the image setup explicitly.

I've not drilled deep into this but wonder if that would mean that the quality would be set explicitly with the $editor->set_quality( $quality ) method for each sub-size that defines the quality via add_image_size()? or that would still mean modifying the default case, where it's not set explicitly as currently done.

Here are results from the plugin directory for the quality filters:

jpeg_quality (2129):

https://wpdirectory.net/search/01G9K09V5X6R46QKCWF0SVDFR6

wp_editor_set_quality (87):

https://wpdirectory.net/search/01G9JXGDA1RQXTHP2SF2QMMCXP

just to have them in the ticket for reference.

@Mte90 commented on PR #2189:


13 months ago
#22

any updates for this one?

#23 @Mte90
6 months ago

Any hope for this one to get merged?

Note: See TracTickets for help on using tickets.