Opened 3 years ago
Last modified 24 hours ago
#54648 accepted enhancement
Improve filter to enable setting quality for each subsize image
Reported by: | Mte90 | Owned by: | adamsilverstein |
---|---|---|---|
Milestone: | Future Release | Priority: | normal |
Severity: | normal | Version: | |
Component: | Media | Keywords: | has-patch needs-unit-tests changes-requested |
Focuses: | performance, sustainability | 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 (37)
#2
@
3 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
@
3 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
@
3 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.
3 years ago
#5
- Keywords has-patch added; needs-patch removed
Trac ticket: https://core.trac.wordpress.org/ticket/54648
#7
@
3 years 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
@
3 years 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:
↓ 10
@
3 years 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
@
3 years 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
@
3 years 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
@
3 years 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
@
3 years 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.
3 years ago
#15
follow-up:
↓ 16
@
2 years ago
I'm not up to date on this, but I just recalled playing with this many years ago (2014) here
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:
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:
in this method:
public function get_quality() { if ( ! $this->quality ) { $this->set_quality(); } return $this->quality; }
#16
in reply to:
↑ 15
@
2 years 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
@
2 years 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.
This ticket was mentioned in Slack in #core-media by adamsilverstein. View the logs.
2 years ago
This ticket was mentioned in Slack in #core-media by adamsilverstein. View the logs.
2 years ago
#20
@
2 years 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
@
2 years 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.
#24
@
8 months ago
- Focuses sustainability added
I think that is part of sustainability, as we are talking to compress and save resources/bandwidth based on the image size.
This ticket was mentioned in Slack in #core-performance by mukeshpanchal27. View the logs.
2 months ago
This ticket was mentioned in Slack in #core-performance by mukeshpanchal27. View the logs.
6 weeks ago
@mukesh27 commented on PR #2189:
6 weeks ago
#30
@adamsilverstein PR needs to update.
This ticket was mentioned in Slack in #core-performance by mukeshpanchal27. View the logs.
4 weeks ago
#33
@
4 weeks ago
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
Was also thinking if this would make sense to have in core (different quality settings depending on sub-size dimensions)? Seems it would benefit sites that have a lot of thumbnails/small images like shops, etc.
This ticket was mentioned in Slack in #core-performance by mukeshpanchal27. View the logs.
2 weeks ago
#35
@
2 weeks ago
This ticket was discussed in today’s Performance bug scrub.
@joemcgill will take a look and move it forward for commit.
#36
@
2 weeks ago
- Milestone changed from 6.7 to Future Release
I spent some time reviewing PR #2189 today and while the change itself is fine, it doesn't seem to address the requirements of this issue. I have confirmed that the set_quality()
method only gets called when the image editor is loaded and that value persists for any sub-size that is generated from the original image.
To address this issue, I think we would need to add some logic to the make_subsize()
methods to set the quality for each sub-size before they are resized. Given that we're this close to Beta 1, I'm going to punt this to a future release and it can be pulled back into a future milestone whenever someone is planning to work on it further.
All the workflow keywords still seem valid.
#37
@
24 hours ago
@joemcgill thanks for reviewing. Indeed when testing the previous PR it didn't work as expected.
Instead, we need to apply the filter and the quality right before resizing the image, before or after we have calculated the final dimensions. This is in the resize method of both Imagick and GD.
I have updated the PR to make this change and verified the filter now passes each image size, and that the returned quality is applied when creating the sub sized images.
The PR still needs unit tests - to test manually, follow these steps:
Test each size is passed to the filter:
- Add some logging based on the filter to verify each size passed to the filter:
<?php function log_calls_to_wp_editor_set_quality( $default_quality, $mime_type, $size ) { error_log( json_encode( $size, JSON_PRETTY_PRINT ) ); return $default_quality; } add_filter( 'wp_editor_set_quality', 'log_calls_to_wp_editor_set_quality', 10, 3 );
- Upload an image and verify each size is logged.
Test quality can be applied per size
- Add a filter on
wp_editor_set_quality
and return a very low quality for specific sizes.
For example, this will output any image 1000 pixels or wider at a very low 5 quality.
<?php function log_calls_to_wp_editor_set_quality( $default_quality, $mime_type, $size ) { if ( $size[0] >= 1000 ) { return 5; } return $default_quality; } add_filter( 'wp_editor_set_quality', 'log_calls_to_wp_editor_set_quality', 10, 3 );
- Review the output images, note that all images 1000 pixels wide or larger are very low quality.
Q: Right now I am applying the filter after the final image size is calculated, passing the calculated size which might include a crop. We could instead pass the "requested" size which might be better because it would always match the image size dimensions What do you think?
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%:
HDPI 50kb 1280x854 - quality 35%:
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