Follow-up to #2260061: Responsive image module does not support sizes/picture polyfill 2.2

- Adjusts theme('responsive_image') to require the caller to provide a 'width' and 'height', like theme('image_style') does. The 95% use case caller (the image field formatter) passes the dimensions anyway (the Imageitem has them).
This improves consistency bewteen responsive and non-responsive styles.

- Improves perf by removing the code that repeatedly reads the dimensions from the file. We stopped doing that in D7 (IIRC) for image styles for performance (reading dimensions on each output can be nastly if the file is not local ?)
Also, removes the need to create an ImageInterface object for the file altogether.

- As a side effect, strramlines a bit the logic between template_preprocess_responsive_image_formatter() responsive_image_build_source_attributes() and how they work together.

- Adjusts the phpdoc for responsive_image_build_source_attributes() accordingly, and fixes a couple nits while we're in there :-)

Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Task: Improve performance and consistency of the theme function responsive images
Issue priority Not critical (but the perf impact for repeatedly reading dimensions from non-local files might be noticeable ?)
Prioritized changes The main goal of this issue is performance (and streamlining some code)
Disruption Disruptive for contributed modules that call theme('responsive_image') directly, they now need to pass the explicit 'width' and 'height' of the image - should be pretty limited, the main entry point for this is Core's ResponsiveImageFormatter

Original report by [username]

I think we can still streamline responsive_image_build_source_attributes() a bit, but this can totally happen in a followup.

This needs a bit more information ;-)

CommentFileSizeAuthor
#85 2423743-85.patch10.54 KBNitin shrivastava
#83 2423743-83.patch10.51 KBmrinalini9
#81 reroll_diff_79-81.txt7.37 KBMedha Kumari
#81 2423743-81.patch10.36 KBMedha Kumari
#79 interdiff_78_79.txt3.84 KBameymudras
#79 2423743-79.patch10.47 KBameymudras
#78 reroll_diff_55-78.txt11.97 KBsahil.goyal
#78 2423743-78.patch8.24 KBsahil.goyal
#55 streamline_responsive_image-2423743-55.patch10.63 KBvprocessor
#55 streamline_responsive_image-2423743-55.interdiff.txt546 bytesvprocessor
#50 streamline_responsive_image-2423743-50.patch10.52 KBvprocessor
#45 i2423743-45.patch10.59 KBJelle_S
#43 2348255-51.patch7.73 KBJelle_S
#40 i2423743-40.patch10.59 KBJelle_S
#37 interdiff.txt1.49 KByched
#37 2423743_resp_img_followup-37.patch10.59 KByched
#34 interdiff.txt1004 bytesyched
#34 2423743_resp_img_followup-34.patch11.05 KByched
#30 interdiff-28-30.txt534 bytesJelle_S
#30 2423743_resp_img_followup-30.patch10.44 KBJelle_S
#28 2423743_resp_img_followup-28.patch10.18 KBJelle_S
#24 streamline-2423743-24.patch10.7 KBJeroenT
#20 interdiff.txt3.99 KByched
#20 2423743_resp_img_followup-20.patch10.64 KByched
#16 interdiff.txt4.39 KByched
#16 2423743_resp_img_followup-16.patch8.3 KByched
#14 interdiff.txt1.73 KByched
#12 2423743_resp_img_followup-12.patch7.12 KByched
#1 2423743_resp_img_followup-1.patch7.05 KByched

Issue fork drupal-2423743

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

yched’s picture

Patch mostly moves the "image dimensions" logic, currently split between template_preprocess_responsive_image() and responsive_image_build_source_attributes() (which then just redoes the same logic for all source tags), all the way into the former.

This makes it clearer that responsive_image_build_source_attributes() only needs a $dimensions array with 'width' and 'height', not the full $variables array from template_preprocess_responsive_image() with lots of other stuff it doesn't use.

Then, this clarifies that I don't understand that logic to begin with :-) :
- We allow calling #theme 'responsive_image' passing forced, pre-defined width and height.
- the beginning of template_preprocess_responsive_image() has a comment saying "if present, we'll output them", but that seems misleading: unlike what theme_image() does with "flat" imgs, we don't output those as 'width' / 'heigth' attributes in the HTML
- Those dimensions are forced as "the dimensions of the original image" to compute the resulting pixel width of the various image derivatives used in the srcset.

So I'm confused why would you would want to pass arbitrary dimensions there, other than the ones of the actual original image ? That sounds like it would totally mess the resulting widths in the srcset ?

Patch then does a couple other cleanups I found along the way:
- Adds a couple comments for balance, and adjust those about the "fallback image"
- Visually aligns the two mappings added in the code sample in the phpdoc for responsive_image_build_source_attributes()
- in that same code sample, changes the 'sizes_image_styles' to a simple numeric array without the duplicate key/values (the config schema says it's a sequence, so it's the only thing that's ever going to get saved anyway ?)
- Fixes the @return doc (a single Attribute object rather than an array now)

Status: Needs review » Needs work

The last submitted patch, 1: 2423743_resp_img_followup-1.patch, failed testing.

yched’s picture

Status: Needs work » Postponed

Oh, of course, patch is rolled on top of #2260061: Responsive image module does not support sizes/picture polyfill 2.2, so it's out of reach for the tesbot for now.

Wim Leers’s picture

Status: Postponed » Active

#2260061: Responsive image module does not support sizes/picture polyfill 2.2 got committed (YAY!), hence this is now unblocked :)

Wim Leers’s picture

Status: Active » Needs review

Let's see if the patch applies.

attiks’s picture

Issue tags: +Needs reroll
yched’s picture

Issue tags: -Needs reroll

Seems to apply OK after all :-)

yched’s picture

Side note: #2424697: ResponsiveImageFormatter throws an exception on node preview
This predates #2260061: Responsive image module does not support sizes/picture polyfill 2.2, but the right people are likely subscribed here, so, shameless plug :-)

Wim Leers’s picture

Assigned: yched » attiks

Assigning to @attiks (I can't assign to @Jelle_S — do you want to officially become a maintainer of the responsive_image module, Jelle_S? :)) to gather a reply to @yched's questions in #1.

Jelle_S’s picture

Status: Needs review » Needs work

@Wim I'd be honoured!

So I'm confused why would you would want to pass arbitrary dimensions there, other than the ones of the actual original image ? That sounds like it would totally mess the resulting widths in the srcset ?

You're absolutely right! It's a leftover from old code. When I first wrote theme_picture() (back when it was still picture.module) I started off with theme_image() as the base code. That in combination with some old outdated specs of the <picture> and <source> tags resulted in the code using the passed dimensions. It should be removed now. I missed it in the big "Responsive Image Cleanup Patch" (#2260061: Responsive image module does not support sizes/picture polyfill 2.2), sorry about that...

So I guess that makes this NW.

yched’s picture

Status: Needs work » Needs review
FileSize
7.12 KB

@Jelle_S thanks for the pointer.

Something like this then ?

[edit; interdiff is in #14]

yched’s picture

Although, looking at how things work for "flat" image styles, it looks like theme 'image_style' *requires* passing the image dimensions.
template_preprocess_image_style() just takes those and directly passes them to $style->transformDimensions() without trying to figure them out from the original image.

Maybe we should just do the same for consistency ?

yched’s picture

FileSize
1.73 KB

Oops, forgot the interdiff for #12

attiks’s picture

+++ b/core/modules/responsive_image/responsive_image.module
@@ -170,24 +170,21 @@ function theme_responsive_image_formatter($variables) {
+  if (!empty($variables['width']) && !empty($variables['width'])) {
+    $dimensions = [
+      'width' => $variables['width'],
+      'height' => $variables['height'],
+    ];
+  }
+  else {
+    $dimensions = [
...
+  }

I think all of this should go, width and height should be removed from variables as well.

We need the exact dimensions of the original image to calculate the sizes of the derivatives.

yched’s picture

@attiks : Actually, theme 'image_style' / template_preprocess_image_style() work the way they do (require the caller to pass the source image dimensions, don't bother trying to read them from the file) because the dimensions are stored in ImageItem field values (see ImageItem::preSave()), and are thus already available.

Displaying a derivative image in an Image field formatter is the 95% use case for image styles output, other use cases need to fetch the image dimensions themselves before calling theme 'image_style'.

So if we did #15 for responsive image styles, that would be a perf regression, because template_preprocess_responsive_image() would perform i/o operations to read the image dimensions from the file while we already have them available in the ImageItem.
That would also be the opposite of how theme 'image_style' works. Keeping image_style and responsive_image_style consistent is IMO a cognitive and maintenance win.

Attached patch:
- Requires image dimensions to be passed, like theme 'image_style' does,
- Then, no need to go through the Image object to grab the extension, we can call pathinfo() on $variables['uri']. This way, no need to go through the 'image.factory' service to create an Image object, the information passed by the caller are enough.
- Additional doc fix : AFAICT the 'image_style' param for theme_responsive_image_formatter() is not optional, template_preprocess_responsive_image() assumes there is one.

yched’s picture

Side note: looking at the code at this point, it might make sense to turn responsive_image_build_source_attributes() back into "handle all mappings" - thus reverting one of the last refactors I suggested in #2260061: Responsive image module does not support sizes/picture polyfill 2.2 ;-). The logic around "dimensions" was a bit more confusing back then...

yched’s picture

Status: Needs review » Needs work

The last submitted patch, 16: 2423743_resp_img_followup-16.patch, failed testing.

yched’s picture

Status: Needs work » Needs review
FileSize
10.64 KB
3.99 KB

Doh.
- arguments order mismatch
- there was still code that used the $image object to get the uri.

The # of parameters for responsive_image_build_source_attributes() make more arguments for #17. Let's get back to green first.

Status: Needs review » Needs work

The last submitted patch, 20: 2423743_resp_img_followup-20.patch, failed testing.

RainbowArray’s picture

Issue tags: +Needs reroll

We need a reroll on this. It looks like this was mostly ready and just needed some reviews? Would be good to get this cleaned up.

JeroenT’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
10.7 KB

Created reroll.

JeroenT’s picture

Issue tags: +drupaldevdays
star-szr’s picture

RainbowArray’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll

Now that #2123251: Improve DX of responsive images; convert theme functions to new #type element has been committed, this will need a reroll. Some of this may no longer be necessary since the DX was changed around a fair amount in that patch.

Jelle_S’s picture

Assigned: attiks » Unassigned
Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
10.18 KB

Rerolled the patch.

Status: Needs review » Needs work

The last submitted patch, 28: 2423743_resp_img_followup-28.patch, failed testing.

Jelle_S’s picture

As said in #16: Passing the width and height is now required for theming responsive images.

Jelle_S’s picture

Status: Needs work » Needs review
yched’s picture

yched’s picture

Issue summary: View changes
yched’s picture

Updated the IS, added a beta evaluation.

Also, additional cleanup of a couple stale "use" statements, no need for ImageInterface anymore :-)

attiks’s picture

Status: Needs review » Reviewed & tested by the community

#34 Thanks, looks good to me and is indeed a performance improvement.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/modules/responsive_image/responsive_image.module
    @@ -204,25 +200,22 @@ function template_preprocess_responsive_image(&$variables) {
      * can contain image style mappings of mixed types (both 'image_style' and
      * 'sizes'). For example:
      * @code
    - * $responsive_img_style = ResponsiveImageStyle::create(array(
    + * $responsive_img_style = ResponsiveImageStyle::create([
      *   'id' => 'style_one',
      *   'label' => 'Style One',
      *   'breakpoint_group' => 'responsive_image_test_module',
    - * ));
    - * $responsive_img_style->addImageStyleMapping('responsive_image_test_module.mobile', '1x', array(
    + * ])
    + * ->addImageStyleMapping('responsive_image_test_module.mobile', '1x', [
      *   'image_mapping_type' => 'image_style',
      *   'image_mapping' => 'thumbnail',
    - * ))
    - * ->addImageStyleMapping('responsive_image_test_module.narrow', '1x', array(
    + * ])
    + * ->addImageStyleMapping('responsive_image_test_module.narrow', '1x', [
      *   'image_mapping_type' => 'sizes',
    - *   'image_mapping' => array(
    + *   'image_mapping' => [
      *     'sizes' => '(min-width: 700px) 700px, 100vw',
    - *     'sizes_image_styles' => array(
    - *       'large' => 'large',
    - *       'medium' => 'medium',
    - *     ),
    - *   ),
    - * ))
    + *     'sizes_image_styles' => ['large', 'medium']
    + *   ],
    + * ])
      * ->save();
    

    Changing to the short array syntax is out of scope and makes the patch harder to review.

  2. +++ b/core/modules/responsive_image/responsive_image.module
    @@ -311,26 +304,23 @@ function template_preprocess_responsive_image(&$variables) {
    -function responsive_image_build_source_attributes(ImageInterface $image, array $variables, BreakpointInterface $breakpoint, array $multipliers) {
    ...
    +function responsive_image_build_source_attributes($uri, array $dimensions, $extension, BreakpointInterface $breakpoint, array $multipliers) {
    

    Why change the $image to $uri - seems unnecessary.

  3. +++ b/core/modules/responsive_image/responsive_image.module
    @@ -371,13 +361,13 @@ function responsive_image_build_source_attributes(ImageInterface $image, array $
    -  $source_attributes = new \Drupal\Core\Template\Attribute(array(
    +  $source_attributes = new Attribute(array(
    

    Out of scope

I'm not sure I see the point of this issue - since nothing that uses the responsive_image template is changing I assume that everything already provides dimensions - and therefore will this actually have any performance impact. There does not seem to be any proof of this impact on the issue.

yched’s picture

Status: Needs work » Needs review
FileSize
10.59 KB
1.49 KB

1. reverted the array() / [] changes (but kept the re-alignment of the two calls to addImageStyleMapping() in the code sample)
2. $image doesn't exist anymore, the point of this patch is that there is no need to create an ImageInterface object :-)
3. sure - but unlike the array() / [] changes mentioned in 1., it hardly complicates patch review ? Temptatively leaving that in.

since nothing that uses the responsive_image template is changing I assume that everything already provides dimensions - and therefore will this actually have any performance impact. There does not seem to be any proof of this impact on the issue

Yes, only the ResponsiveImageFormatter currently calls 'responsive_image' in core, and it does already pass the dimensions, and thus does not need any change.
But even in that case, template_preprocess_responsive_image-) currently needlessly creates an Image object, and responsive_image_build_source_attributes() repeatedly calls getDimensions() on it [edit: sorry, that was a little hasty. Current code doesn't read $image dimensions if they are already passed in. But the $image is not needed to begin with]

Status: Needs review » Needs work

The last submitted patch, 37: 2423743_resp_img_followup-37.patch, failed testing.

Jelle_S’s picture

Status: Needs work » Needs review
Issue tags: -drupaldevdays
FileSize
10.59 KB

Reroll.

Status: Needs review » Needs work

The last submitted patch, 40: i2423743-40.patch, failed testing.

The last submitted patch, 40: i2423743-40.patch, failed testing.

Jelle_S’s picture

Status: Needs work » Needs review
FileSize
7.73 KB

Minor fix (forgot to pass the $uri argument to responsive_image_get_image_dimensions). Let's see what the bot thinks (assuming the bot is fixed by now...)

RainbowArray’s picture

I think you mixed in some work from another issue. There are changes to the template here that I don't think belong.

Jelle_S’s picture

FileSize
10.59 KB

Oops! You're right, my bad. That patch is for an entirely different issue. Good to know it's green though ;-) . New patch without the code from the other issue.

RainbowArray’s picture

Version: 8.0.x-dev » 8.1.x-dev

Might be worth looking to see if any of these improvements could go in under the hood in 8.1.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.0-beta1 was released on March 2, 2016, which means new developments and disruptive changes should now be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Wim Leers’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll

Would be great if we could get this going again. No longer applies to 8.1.x

vprocessor’s picture

Assigned: Unassigned » vprocessor
vprocessor’s picture

vprocessor’s picture

Assigned: vprocessor » Unassigned
Status: Needs work » Needs review
Issue tags: -Needs reroll
Wim Leers’s picture

Thanks! :)

Status: Needs review » Needs work

The last submitted patch, 50: streamline_responsive_image-2423743-50.patch, failed testing.

vprocessor’s picture

Assigned: Unassigned » vprocessor

Checking for errors

vprocessor’s picture

Assigned: vprocessor » Unassigned
Status: Needs work » Needs review
FileSize
546 bytes
10.63 KB

>>fail: [PHP Fatal error] Line 402 of core/modules/responsive_image/responsive_image.module:
>> Class 'Attribute' not found

fixed

interdiff and patch added

vprocessor’s picture

andypost’s picture

Code looks great, +1 rtbc here
But there's API change and #36 points about scope...

+++ b/core/modules/responsive_image/responsive_image.module
@@ -138,25 +138,19 @@ function template_preprocess_responsive_image_formatter(&$variables) {
- *   - width: The width of the image (if known).
- *   - height: The height of the image (if known).
+ *   - width: The width of the image.
+ *   - height: The height of the image.

@@ -344,27 +337,23 @@ function template_preprocess_responsive_image(&$variables) {
-function responsive_image_build_source_attributes(ImageInterface $image, array $variables, BreakpointInterface $breakpoint, array $multipliers) {
...
+function responsive_image_build_source_attributes($uri, array $dimensions, $extension, BreakpointInterface $breakpoint, array $multipliers) {

this requires change record with example code for contrib maintainers

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

catch’s picture

Status: Needs review » Needs work
Issue tags: -Needs framework manager review +Needs release manager review

So on the one hand responsive_image_build_source_attributes() looks like just a helper for the preprocess that should have an an underscore on it.

On the other hand it's got very extensive documentation that makes it look like an API function, and it doesn't have an underscore.

So probably we should add a new method, and if possible have responsive_image_build_source_attributes() call that method? Shame to duplicate the code unless we really have to, since this probably should never have been an API function in the first place.

xjm’s picture

#59 sounds good to me. I'd also explicitly deprecate responsive_image_build_source_attributes() with that approach.

vprocessor’s picture

Assigned: Unassigned » vprocessor

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

quietone’s picture

Assigned: vprocessor » Unassigned

Unassigning because it has been 5 years since vprocessor was able to work on this issue.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

catch’s picture

This already had release manager review - we should do this in bc-friendly way.

andypost’s picture

Issue tags: +Needs reroll

Bhanu951 made their first commit to this issue’s fork.

sahil.goyal’s picture

Rerolled the patch for current version and attaching the reroll diff.

ameymudras’s picture

Status: Needs work » Needs review
FileSize
10.47 KB
3.84 KB

Fixed issues with the above patch

Status: Needs review » Needs work

The last submitted patch, 79: 2423743-79.patch, failed testing. View results

Medha Kumari’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
10.36 KB
7.37 KB

Reroll the patch #79 with Drupal 10.1.x.

JeroenT’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll
mrinalini9’s picture

Status: Needs work » Needs review
FileSize
10.51 KB

Rerolled patch #81, please review it.

mrinalini9’s picture

Issue tags: -Needs reroll
Nitin shrivastava’s picture

Fix Custom command failure for #83

Status: Needs review » Needs work

The last submitted patch, 85: 2423743-85.patch, failed testing. View results

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.