Problem/Motivation

The ImageStyle class is a config entity that besides doing the config entity, also has methods that do process images into derivatives.

This is probably the result of the migration of the image system from D7.

As things stand, though, this is causing issues -

  • the entity code is for instance invoking hooks
  • it's difficult for custom/contrib to override parts of the image derivatives generation process, for instance to change the derivative URL/URI, or to pre/postprocess image derivatives

Several issues were filed in that respect, to list some:
#2359443: Allow creating image derivatives from an Image object
#1358896: Flexible scheme and URI for image derivatives
#2940016: Make possible to respond to image derivative creation
#1826362: ImageEffects of the same image style should be able to pass variables between them
#2685905: Refactor ImageStyleDownloadController so derivatives can be generated by contrib modules
#1903190: Allow image style derivatives of private images to be stored on the public file system
#2098247: Provide a hook for ImageStyle::createDerivative
#3218514: [PP-1] Deprecate passing path (instead of true URI) to ImageStyleInterface::buildUri($uri)

Proposed resolution

In #2359443-26: Allow creating image derivatives from an Image object, before stalling, we started discussing decoupling the config entity from the image derivative processing code.

This is implementing that:

  • separate the image derivative processing methods from the ImageStyle into a plugin, that takes ImageStyle as the config entity as in input, like the source URI or the Image object directly
  • deprecate the methods on the ImageStyle, leaving the code that calls the plugin methods instead
  • the plugin implements a fluent API to pass the variables needed and then executes the process required, bieng determining the derivative URI, or creating the derivative image, or getting the safety token
  • keeping full BC

Why a plugin?
So that the default implementation can be overridden e.g. to change the derivative URI calculation logic or to post-process an image derivative - only re-implementing the plugin methods that are necessary without touching the config entity code. But also, plugin allows to have parallel 'derivative processing engines' if someone wants that. For instance, Textimage contrib module has its own formatter and API that could leverage this plugin concept.

Remaining tasks

Review

User interface changes

None

API changes

Several methods deprecatedin ImageStyle, new plugin manager and a default plugin implemented.

Data model changes

None

CommentFileSizeAuthor
#96 2986669-nr-bot.txt90 bytesneeds-review-queue-bot
#76 2986669-nr-bot.txt150 bytesneeds-review-queue-bot
#61 Screen Shot 2021-12-03 at 6.11.00 PM.png235.32 KBdeviantintegral
#61 Screen Shot 2021-12-03 at 6.10.41 PM.png126.76 KBdeviantintegral
#44 interdiff_39-44.txt11.27 KBmondrake
#44 2986669-44.patch162.08 KBmondrake
2986669-43.patch162.08 KBmondrake
interdiff_39-43.txt11.27 KBmondrake
#41 interdiff_39-41.txt10.07 KBmondrake
#41 2986669-41.patch161.45 KBmondrake
#39 interdiff_37-39.txt1.76 KBmondrake
#39 2986669-39.patch159.12 KBmondrake
#37 2986669-37.patch158.05 KBmondrake
#36 2986669-36.patch158.46 KBmondrake
#34 2986669-34.patch158.48 KBmondrake
#34 interdiff_32-34.txt2.05 KBmondrake
#32 interdiff_30-32.txt16.13 KBmondrake
#32 2986669-32.patch158.48 KBmondrake
#30 2986669-30.patch159.4 KBmondrake
#29 2986669-29.patch159.39 KBmondrake
#28 2986669-28.patch160.05 KBmondrake
#28 interdiff_26-28.txt5.97 KBmondrake
#26 2986669-26.patch159.96 KBmondrake
#25 2986669-25.patch159.94 KBmondrake
#22 interdiff_20-21.txt81.46 KBmondrake
#22 2986669-21.patch160.61 KBmondrake
#20 interdiff_16-20.txt2.96 KBmondrake
#20 2986669-20.patch88.03 KBmondrake
#16 interdiff_15-16.txt8.54 KBmondrake
#16 2986669-16.patch89.49 KBmondrake
#15 interdiff_13-15.txt108.09 KBmondrake
#15 2986669-15.patch89.02 KBmondrake
#13 2986669-13.patch75.94 KBmondrake
#13 interdiff_11-13.txt50.23 KBmondrake
#11 interdiff_9-11.txt19.08 KBmondrake
#11 2986669-11.patch72.37 KBmondrake
#9 2986669-9.patch72.16 KBmondrake
#9 interdiff_7-9.txt10.63 KBmondrake
#7 2986669-7.patch71.9 KBmondrake
#2 2986669-2.patch74.05 KBmondrake

Issue fork drupal-2986669

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:

Comments

mondrake created an issue. See original summary.

mondrake’s picture

Status: Active » Needs review
StatusFileSize
new74.05 KB

Kick-off patch.

mondrake’s picture

Title: Proposal: decouple ImageStyle as a config entity and a image derivative processing service » Proposal: decouple ImageStyle into a config entity and an image derivative processing service

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.

mondrake’s picture

Version: 8.9.x-dev » 9.0.x-dev
mondrake’s picture

StatusFileSize
new71.9 KB

Reroll

Status: Needs review » Needs work

The last submitted patch, 7: 2986669-7.patch, failed testing. View results

mondrake’s picture

StatusFileSize
new10.63 KB
new72.16 KB

Fixing failures, changing calls of removed deprecated functions.

mondrake’s picture

Status: Needs work » Needs review
mondrake’s picture

Title: Proposal: decouple ImageStyle into a config entity and an image derivative processing service » Proposal: split ImageStyle into the config entity and a separate image derivative processing service
StatusFileSize
new72.37 KB
new19.08 KB

Thinking a bit about this, I start figuring out that what would be good to have here are 'pipelines', that norm the image derivation tasks, and the pipeline tasks as 'events'. The Image Optimize (or ImageAPI Optimize) module does something like that already. But IMO we need to extend that also to other tasks, like preparing the source image for derivation, post-processing, etc etc. Other modules would benefit (for instance the Imagemagick module needs to copy images stored in remote storage to local disk for processing, and then move the derivative from local file to remote storage - and that is done on the toolkit level right now). Starting something here.

mondrake’s picture

Assigned: Unassigned » mondrake
Status: Needs review » Needs work
mondrake’s picture

StatusFileSize
new50.23 KB
new75.94 KB

Intermediate step - this introduces the pipeline. Variables are set to the pipeline, and when you ask for an output or a task, the relevant subtasks required get recursively executed automatically. Next iteration will be moving the tasks to an event dispatch/subscribe mechanism.

berdir’s picture

Drive-by review

+++ b/core/modules/image/src/Entity/ImageStyle.php
@@ -174,130 +164,37 @@ protected static function replaceImageStyle(ImageStyleInterface $style) {
-    return "$scheme://styles/{$this->id()}/$source_scheme/{$this->addExtension($path)}";
+    @trigger_error('The ' . __METHOD__ . ' method is deprecated since version 9.x.x and will be removed in y.y.y.', E_USER_DEPRECATED);
+    return \Drupal::service('image.processor')->createInstance('core')
+      ->setImageStyle($this)
+      ->setSourceImageUri($uri)
+      ->getDerivedImageUri();
   }
 

I'm not sure if I like the API, having a service with a state like that is a IMHO a bad design.

maybe it should be like a factory that creates an object for each image style + source image?

Another thing, in light of #2669074: Convert file_create_url() & file_url_transform_relative() to service, deprecate it, we might want to make this API return URL objects, this currently not doing that is one reason why we had to go back to having 3 different methods on the new service there.

mondrake’s picture

Title: Proposal: split ImageStyle into the config entity and a separate image derivative processing service » Proposal: split ImageStyle into the config entity and a separate event-based image processing service
Status: Needs work » Needs review
StatusFileSize
new89.02 KB
new108.09 KB

@Berdir thanks for the review and for the interest in this effort :)

This patch is introducing events and and an event subscriber to manage the derivatives generation process. The pipeline states have been removed.

Re. #2669074: Convert file_create_url() & file_url_transform_relative() to service, deprecate it I made a first step here to return an URL object from the pipeline in case of dirty URLs; in case of clean URLs (which is I believe 99.99% of the cases), it's still returning a string - I am not sure how to move on here since the API is calling file_create_url which is itself returning a string. Is this a catch 22?

Interdiff is likely useless.

mondrake’s picture

Related issues: +#2669074: Convert file_create_url() & file_url_transform_relative() to service, deprecate it
StatusFileSize
new89.49 KB
new8.54 KB

Now, the API always returns an URL object for the derivative image URL, no longer strings. To get there I introduced a new method to Url, Url::fromExternalUrl, which takes as input the output of file_create_url which is a string. If in the refactoring in #2669074: Convert file_create_url() & file_url_transform_relative() to service, deprecate it we could get Url objects instead, that would probably not be needed.

bradjones1’s picture

Version: 9.0.x-dev » 9.1.x-dev
mondrake’s picture

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

@bradjones1 yes this is for no sooner than 9.1.x, but let's keep the version in the issue to 9.0.x ATM so patches can be tested by the bot, 9.1.x branch is not updated.

andypost’s picture

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

Sounds like media already made kind of it to redo thumbnails processing, and features are for 9.1

mondrake’s picture

StatusFileSize
new88.03 KB
new2.96 KB

Looks like we do not need an additional method in Url, Url::fromUri can do, albeit with a little bit more overhead than a dedicated method. In any case, as mentioned in #16, if in the refactoring in #2669074: Convert file_create_url() & file_url_transform_relative() to service, deprecate it we could get Url objects directly from the service methods, the overhead may be skipped.

mondrake’s picture

Issue summary: View changes
mondrake’s picture

Title: Proposal: split ImageStyle into the config entity and a separate event-based image processing service » Split ImageStyle into the config entity and a separate event-based image processing service
StatusFileSize
new160.61 KB
new81.46 KB

Here, with all deprecated calls removed and without deprecation silencers.

For three test classes:

  • Drupal\Tests\image\Functional\FileMoveTest
  • Drupal\Tests\image\Functional\ImageDimensionsTest
  • Drupal\Tests\image\Functional\ImageStylesPathAndUrlTest

I marked existing tests as legacy and rewrote them refreshing the logic to PHPUnit - that's why the size of the patch increases a bit.

mondrake’s picture

Assigned: mondrake » Unassigned
mondrake’s picture

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

Status: Needs work » Needs review
Issue tags: -Needs reroll
StatusFileSize
new159.94 KB

Rerolled.

mondrake’s picture

StatusFileSize
new159.96 KB

Rerolled.

Status: Needs review » Needs work

The last submitted patch, 26: 2986669-26.patch, failed testing. View results

mondrake’s picture

Status: Needs work » Needs review
StatusFileSize
new5.97 KB
new160.05 KB

Fixing test failures.

mondrake’s picture

StatusFileSize
new159.39 KB

Reroll.

mondrake’s picture

StatusFileSize
new159.4 KB

reroll

Status: Needs review » Needs work

The last submitted patch, 30: 2986669-30.patch, failed testing. View results

mondrake’s picture

Status: Needs work » Needs review
StatusFileSize
new158.48 KB
new16.13 KB

deprecation fixes

Status: Needs review » Needs work

The last submitted patch, 32: 2986669-32.patch, failed testing. View results

mondrake’s picture

mondrake’s picture

Status: Needs work » Needs review
mondrake’s picture

StatusFileSize
new158.46 KB
mondrake’s picture

StatusFileSize
new158.05 KB

Status: Needs review » Needs work

The last submitted patch, 37: 2986669-37.patch, failed testing. View results

mondrake’s picture

Status: Needs work » Needs review
StatusFileSize
new159.12 KB
new1.76 KB

Addressing failures.

Status: Needs review » Needs work

The last submitted patch, 39: 2986669-39.patch, failed testing. View results

mondrake’s picture

Status: Needs work » Needs review
StatusFileSize
new161.45 KB
new10.07 KB

Status: Needs review » Needs work

The last submitted patch, 41: 2986669-41.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mondrake’s picture

mondrake’s picture

StatusFileSize
new162.08 KB
new11.27 KB

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.

mondrake’s picture

Changed issue to Merge Request workflow.

mondrake’s picture

Rerolled

bradjones1’s picture

Status: Needs review » Needs work

Per latest MR test failures; looks not necessarily related to the feature but the test setup.

mondrake’s picture

Status: Needs work » Needs review

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.

mondrake’s picture

Issue summary: View changes
mondrake’s picture

mondrake’s picture

Rerolled

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

deviantintegral’s picture

I updated from 9.4.x and made a few very minor fixes while reviewing.

  1. While the event having setVariable and related methods is certainly flexible, I don't like how it's similar to arrays in that a single typo in a key can be hard to debug. My first thought is that those should be pulled out into an interface, and then instead of ->hasVariable() ? ... it changes to $pipeline instanceof DerivativeImageUrlInterface.
  2. It feels odd to have the Derivative plugin, but only a single implementation, and no interface. I think to meet the "Why a plugin" goals in the issue summary we at least need an interface. Once that's done, I think the \Drupal::service(..) calls in the ImageStyle entity should be pulled out into a method to assist type checking and method autocomplete in IDEs.
  3. The ImageDerivativeSubscriber class seems rather large, with many constructor arguments and imports. I noticed that flushing is the only place using the module handler, so perhaps that and other methods could be split out into a few different subscribers.
  4. It would be good to split ImageProcessException, perhaps make it a base exception with a few subclasses like MissingSourceException, CachedImageExistsException, etc.
  5. Reading the interface comments at \Drupal\image\ImageStyleInterface::createDerivative() for example, it's not clear what to do instead of that method. Further docs could help here.

I haven't reviewed any test code. The only failure is due to calling a newly marked deprecated method.

Regardless of the above, I would like to take a pass at implementing what's here to support alternative image processing methods. I have time reserved December 3rd, so if any of the above are clear next steps I can work on them then.

bradjones1’s picture

I have time reserved December 3rd

This is next-level organization. I am in awe.

mondrake’s picture

Great to see more hands and eyes on this patch @deviantintegral @bradjones1!

#56 sounds like an excellent plan. Honestly I believe you know more of this patch than what I remember of it ATM...

WRT interfaces I recall thinking that we would need some, but then just deciding to post the patch as was to see what feedback would bring in terms of changes before starting that piece.

In general I see the combination of plugin + events may seem overwhelming but my rationale was to get to enable overriding at the highest granularity possible - so that folks can override/provide alternatives both for the derivative generation process overall but also for single steps of it (for instance, defining the target URL, or calculate a security token).

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.

deviantintegral’s picture

Status: Needs review » Needs work

I only have time because Lullabot gives us a few days to dedicate each year, and I've not used them yet! Thanks for the kind words.

I've got several commits locally that refactors to use interfaces in many places, but I realized there's a gap in the current architecture.

In general I see the combination of plugin + events may seem overwhelming but my rationale was to get to enable overriding at the highest granularity possible

Agreed! However, as is it's not actually possible for a contrib module to easily replace what core's image styles do, but only alter them. For example, in the current patch's buildUri method in ImageStyle and similarly in template_preprocess_image_style():

  /**
   * {@inheritdoc}
   */
  public function buildUri($uri) {
    @trigger_error('The ' . __METHOD__ . ' method is deprecated since version 9.x.x and will be removed in y.y.y.', E_USER_DEPRECATED);
    return \Drupal::service('image.processor')->createInstance('derivative')
      ->setImageStyle($this)
      ->setSourceImageUri($uri)
      ->getDerivativeImageUri();
  }

Since the derivative plugin is completely tied to core's image style implementation, it has functionality to set clean URLs, deal with derivative tokens, etc. An external image service may not have any of these concepts at all.

As well, the "check for null, dispatch an event, set the value" loop seems like an easy way to accidentally introduce recursion (which I've done locally a few times now).

My thinking is that we reorganize the plugin and events so that:

  1. The image.processor service becomes a normal service and not a plugin type. It would only have very basic methods, like setImageStyle, getDerivativeImageUrl, and so on.
  2. The processor service loads all implemented plugins (like the current derivative plugin), and calls a method to determine if that plugin can handle the request.
  3. Each plugin type can choose to expose events for itself, with derivative being the example. That way, contrib modules that need just very targeted changes can listen to those events, and site-specific code that doesn't need events (or contrib that doesn't want to allow altering) can skip them entirely.
deviantintegral’s picture

Status: Needs work » Needs review
StatusFileSize
new126.76 KB
new235.32 KB

All unit and kernel tests, and at least some functional tests are passing. Let's see how far it gets.

Regardless, I've completed significant work on the branch, though I'm sure it still needs work. I've hacked in some dependencies via \Drupal, and docs are missing or need to be updated. As well, Derivative needs fresh eyes to break it down, and the events that are fired need review and likely tests.

The Drupal\image\Pipeline namespace contains different interfaces that represent various known features of image style rendering systems. For example, a system may only have the concept of derivative URLs (keeping source images opaque to clients), or have security tokens like Amazon S3's signed URLs or Drupal's itok parameter. The goal is that each plugin would implement some subset of these interfaces, with the current Derivative plugin implementing all of them (since it's very fully featured!).

As well, I completed the switch from using events as an API, to using them more directly as an extension mechanism.

Finally, in solving the issue of determining what pipeline to use, I decided that making it a configurable option for image styles made the most sense. It currently exposes the available plugins when configuring the image styles, but I think we'd hide it if there's only one. This would allow existing sites to use one or more image providers, which could be especially helpful during migrations between them. Which reminds me, that totally needs a config dependency on any modules that expose pipeline plugins.

And the current hierarchy of the interfaces:

I have two more days available to work on this, currently set for December 9 and 10th.

mondrake’s picture

I have not seen the patch in details, but rationale sounds solid to me. Good progress!

[...] in solving the issue of determining what pipeline to use, I decided that making it a configurable option for image styles made the most sense.

This is something I'd like to question. To me, it should be the other way round - the pipeline run should have configuration where the image style is one of the variables. IMO an image style should only be a collection of image manipulation operations, nothing more.

Let's dream a bit and think a possibile scenario:

A site collecting landscape photography.

The reqs are to

  1. upload original JPEG photos, full size, after resolving potential orientation aspects and editing the metadata to add a copyright exif tag and ensuring GPS coordinates of the photo taken exist, to a private storage on AWS so that they cannot be accessed publicly;
  2. show a PNG thumbnail in the image upload widget to users allowed to upload the photos, stored in private schema;
  3. generate publicly visible derivatives, downscaled and watermarked with copyright text taken from exif metadata, in three formats AVIF, WEBP, PNG so that browsers can decide the best format to retrieve, from the public schema.

What I'd fancy about is this would be defined respectively

  1. in the field settings of the image field, with an 'upload' pipeline configured appropriately
  2. in the item FieldWidget, configured to use a 'derivative' pipeline that uses a 'thumbnail' image style, and possibly defines the output format in the pipeline configration instead of a convert effect in the image style
  3. in the FieldFormatter, configured to use a 'multiderivative' pipeline that uses an image style for scaling and watermarking, and defines the output formats in the configuration in such a way that image toolkits would directly save the derivative in the three different formats after the image has been manipulated instead of running three separate 'derivative' pipelines.

Now, I won't say that Drupal core should support all this (it won't), nor that we should do all in this issue (it will never go through), but here we could agree on the framework and a base implementation of 'derivative' that could be compatible with these cases.

mondrake’s picture

Status: Needs review » Needs work

NW to fix the CS issues

andypost’s picture

When such kind of configuration for plugins discussed (#62) wanna point very old issues, I bet styles plugins could get benefit from that

- #2011038: Use Context system for actions
- #2347023: Move processing plugin context to data processors

deviantintegral’s picture

Summary: Unless I hear otherwise, I will likely work tomorrow on refactoring pipelines to be bound to an image toolkit, instead of an image style.

For context, my own goal is for core to support what isn't viable today: Using Drupal's image style system to render images with a remote API like Cloudinary or Fastly Image Optimizer. If that is possible, for many sites it reduces or eliminates the need for fast shared disk storage, simplifying cloud and containerized deployments. While there is a cloudinary D8 module, in practice it doesn't work and couldn't be made to work without core hacks. We ran into similar challenges years ago with S3 support for images.

This is something I'd like to question. To me, it should be the other way round - the pipeline run should have configuration where the image style is one of the variables

That would mean a separate config form for pipeline instances? It feels like an additional level of abstraction that's not got a clear benefit to me.

To go through all of the Image concepts we have as of the current patch:

  1. Image toolkits: Typically GD or ImageMagick. These are bound to the source image in the Image class. Part of what we're addressing is decoupling the "toolkit" used for the source image and any derivative images.
  2. Image effects: These just manipulate data. It's the toolkit that "renders" effect data into a derivative. These are great in that they aren't tied to toolkits, but difficult in that they can't know if a given image's toolkit can successfully apply an effect.
  3. Image styles: A collection of image effects.
  4. Image pipelines: A plugin and a set of interfaces that define the types of features supported for making derivative images.

In core as it exists today, I think the largest limitation is that image toolkits have no way to communicate to image styles what effect operations they support via the apply method. We have FALSE returns, but those can also happen because of a runtime error. Conceptually, image toolkits also expect to be literally "saving" a file, when most remote APIs just want a URL prepared.

When I look at what has the most coupling, it supports your suggestion that styles are the wrong place to integrate with pipelines.

Instead, what if it was at the toolkit level? Pipelines become invisible to the administrator, and are a way for common toolkits like GD and ImageMagick to share code. I'm imagining a "Local file derivative" (shipped with core) and one or more "remote URL derivatives" (written in contrib).

From an admin perspective:

  1. The site default toolkit is used for image styles like today.
  2. Image styles can override the toolkit used, if more than one toolkit is available.
  3. Someone building image styles can be warned if a given toolkit's derivative plugin doesn't support an effect (or we could hide those effects entirely).

To think through your use cases:

1. upload original JPEG photos, full size, after resolving potential orientation aspects and editing the metadata to add a copyright exif tag and ensuring GPS coordinates of the photo taken exist, to a private storage on AWS so that they cannot be accessed publicly;

This essentially implies "derivatives of derivatives". Or, perhaps a simpler method would be "apply this image style to the field when uploading". Is there any limitation in Drupal's core APIs to implement this in contrib today? I think it could be done by decorating or swapping out the ImageItem plugin, updating the field settings form and calling the image style in \Drupal\image\Plugin\Field\FieldType\ImageItem::preSave(). If this is possible today, then I think this issue should be limited to ensuring we don't break this extension point for contrib.

2. show a PNG thumbnail in the image upload widget to users allowed to upload the photos, stored in private schema;

I'm not clear how this is different from what can be done today. If an image field is created, we can set the image style in the Preview Image Style option.

3. generate publicly visible derivatives, downscaled and watermarked with copyright text taken from exif metadata, in three formats AVIF, WEBP, PNG so that browsers can decide the best format to retrieve, from the public schema.

I don't think this can be done today, in that the scheme kept the same for image styles. If an image is in the private stream wrapper, the derivatives stay there too. Perhaps this would be a new use case for a type of derivative plugin in contrib that extends the proposed "local derivative" plugin that ships in core, but exposes config to change the destination scheme. The multiple formats aspect seems to be handled given the contrib webp module, even if it's not an optimal implementation.

1. in the field settings of the image field, with an 'upload' pipeline configured appropriately

So, could this just be an image style? The uploading aspect would be handled by the selected stream wrapper.

2. in the item FieldWidget, configured to use a 'derivative' pipeline that uses a 'thumbnail' image style, and possibly defines the output format in the pipeline configration instead of a convert effect in the image style

This exposes the coupling to the image toolkit, in that it's toolkits that determine what formats are supported via \Drupal\Core\ImageToolkit\ImageToolkitInterface::getSupportedExtensions. If image formats need to be extracted from image styles, perhaps that's in the future? I think keeping formats as a part of image styles will help constrain scope here.

3. in the FieldFormatter, configured to use a 'multiderivative' pipeline that uses an image style for scaling and watermarking, and defines the output formats in the configuration in such a way that image toolkits would directly save the derivative in the three different formats after the image has been manipulated instead of running three separate 'derivative' pipelines.

This implies a push-based generation of derivatives instead of the pull-based workflow we have today. I think it's important to consider, as there are likely systems out there that benefit from pregenerating derivatives (or actually require it).

bradjones1’s picture

my own goal is for core to support what isn't viable today: Using Drupal's image style system to render images with a remote API like Cloudinary or Fastly Image Optimizer.

I'm not sure if I'm chiming in at a very late date or this is orthogonal to the changes described here, but I have done some work in contrib-land to support what I've called external image styles.

https://www.drupal.org/project/external_image_styles

Also perhaps related, some prior art on supporting splitting the origin image and derivative image storage:

https://www.drupal.org/project/cloud_native_image_styles

I'll have to take a read of the full thread to get a flavor for how this might affect this pattern, but A) you're not the only one interested in this pattern, even if it isn't typical and B) I hope some of this helps at least validate the pattern or perhaps inform an approach that suits different use cases.

deviantintegral’s picture

Thanks for sharing these! I see External Image Styles:

  • Alters the image style entity class using hook_entity_type_alter.
  • Has the same approach as the current MR, where the image style has a reference to the provider or pipeline. One difference is that this MR makes that editable for existing image styles, while external image styles (at least at the UI level) locks it in to create only. I like that, because it sidesteps issues around flushing "stale" images when changing providers.
  • Overrides \Drupal\external_image_styles\Entity\ImageStyle::buildUri and \Drupal\external_image_styles\Entity\ImageStyle::createDerivative to throw a runtime exception if used.

I was curious how likely those methods are to be called in real sites. I see:

  1. Token module calls buildUri to expose the image URI token.
  2. Webform has a WebformImageFile element calling buildUri.
  3. webp module does too (but, perhaps that wouldn't be used with external image styles anyways)
  4. All three modules also call createDerivative.

That leads me to believe that external image styles cannot be used without careful consideration, and for smaller or junior teams they could run into situations where images "break" without a clear reason why.

Cloud Native Image Styles is great! I see how it allows the default filesystem to be flysystem or S3, and derivatives to be stored in container instance storage (instead of shared storage). There'd be a higher temporary disk use since each container or web server would generate it's own cache of images, but I bet that's still way better then the slower performance and cost of something like EFS. This seems like a good solution for sites with containerized deployments, but still wanting to use core's image styles.

So... next steps? I think External Image Styles validates the approach of "the rendering pipeline is tied to the image style", given two of us came up with similar solutions independently. I'm going to work on cleaning up the current MR instead of the refactor I suggested above.

And, as an additional goal for this issue: When merged, a module like External Image Styles should no longer need to override and throw runtime exceptions for methods like buildUri().

mondrake’s picture

Strictly coupling the Image Style with its process pipeline means e.g. in Textimage (that currently uses a core Image Style through an alternative image derivation process), one would have to duplicate the image style used with the core 'derivative' pipeline with the same style used for the 'textimage' pipeline. Unless in Textimage we disregard the pipeline indicated in the image style, but that would sound odd.

deviantintegral’s picture

Since my last push and post, I set up https://github.com/cshum/imagor as an easy to run image service to test an implementation against. I got it working! Meaning, I was able to write a contrib module that used that service for image generation, and show the results in the image style edit form.

However, I found that nearly all of the logic was in the image toolkit implementation, and it basically ignored all the work on image pipelines. Making it work without a custom image toolkit wasn't possible. It makes me think what we may want is an even simpler version of this patch that _just_ makes it so buildUri and friends can work with alternative storage systems, and pipelines become their own later addition. I need to think on that.

I was getting ready to push up the work I did changing ImageStyles to select the toolkit instead of the pipeline... but my laptop disk died. If I have time, I may see about pulling out the commits from backups. If someone was planning on working on this over the next two weeks, let me know and I'll prioritize that.

dieterholvoet’s picture

This would be really useful for the Imgix module, we have had to override the image_style entity class in order to make it work.

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.

andypost’s picture

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

mondrake’s picture

I've rerolled 70ec65f8 and targeted to 10.1.x. in MR 2691.

I started from my latest version before #54, that's what I know and I stand by. I'm leaving MR 105 untouched for further work on @deviantintegral's approach.

mondrake’s picture

Status: Needs work » Needs review
needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new150 bytes

The Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

mondrake’s picture

Rebased.

bradjones1’s picture

cspell error

/var/www/html/core/modules/user/tests/src/Functional/UserPictureTest.php:123:34 - Unknown word (getfile)

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.

mondrake’s picture

Status: Needs work » Needs review

rebased onto 11.x

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs change record

Seems like something that could use a change record.

For any contrib modules that add an image style will they have to do anything or will this work automatically.

mondrake’s picture

Status: Needs work » Needs review

@smustgrave re #81:

Seems like something that could use a change record.

For sure. However, 5 years after this was started, there seems to be little or no interest to move this forward - at least I cannot see reviews in this sense. So, either we simply just won't fix this, or a review in the sense of agreeing on the architecture (or a propositive disagreement) is needed. Writing a CR now would be simply too early/useless. Please leave to NR while we get a review - or close this.

For any contrib modules that add an image style will they have to do anything or will this work automatically.

Not sure I understand. You can add image effect plugins. Or configuration of image styles. Both are not affected by this.

andypost’s picture

Did a first pass of review and wondered do we really need so many events fired while generation happening.

Very probably it affects performance as each event is dispatched (as I got).

Maybe instead it could use less events as some events can carry state of process

smustgrave’s picture

smustgrave’s picture

Status: Needs review » Needs work

For #83

mondrake’s picture

Status: Needs work » Closed (won't fix)

I give up, sorry.

I could think of doing something in contrib, but core should at a minimum split the entity from the image processing code - in a way that it could be overridable. AFAICU it's not possible at the moment.

smustgrave’s picture

Wasn't trying to shoot down the idea. Not something I can speak heavily on though.

mondrake’s picture

No hard feelings, @smustgrave. Just I think after 5 years being open, there's no community interest at carrying this on. So I won't spend time on rerolls.

cmlara’s picture

Status: Closed (won't fix) » Needs review

I really don't think this should be closed.

I have been a bit booked by other tasks so I haven't been able to give it a test run, which I really wanted to do before commenting to hopefully better comment, but given there is a move to close it I feel a need to comment without having done the testing yet.

@andypost in #83 you assert there is a 'probable' performance impact but did not provide a profile report with your post? Can you please attach the generated performance report file for the rest of us to review?

While I've not traced every single event yet in this MR, the majority of the ones I have are only needed when an image derivative doesn't exist, the majority of times images are served from the file on disk. The most common to trigger event I would expect to be the buildUrl() events which need to be fired to generate links to ImageStyles Derivatives, though the flexibility of an event is necessary to allow all the changes that have been requested over the years.

Could some events be combined? Without looking at every line, Maybe? But would it make sense? Most of these events are in response to a specific method being called, if no-one ever calls that method, why invoke the expense of gathering data that no-one will use?

IMHO it would be a shame for this work to be lost especially since #83 didn't provide any objective data to justify moving to needs-work.

larowlan’s picture

Closed the outdated MR

Looking at the patch next

larowlan’s picture

Left some comments on the MR

Keeping at NR and retaining the 'Needs architectural review' tag

mondrake’s picture

Thanks @larowlan for the review - I'll address your points but it may take a while.

mondrake’s picture

Replied to @larowlan's comments, inline.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new90 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

vlad.dancer’s picture

@mondrake, deviantintegral, @bradjones1 thanks a lot for your work!

Just I think after 5 years being open, there's no community interest at carrying this on. So I won't spend time on rerolls.

I'm working on a way of bringing UI to specify image styles beeing used for remote images for IntelligenceBank DAM and I think this issue is a good base for it. Typically DAMs like IB or PicturePark provides a way to generate image derivatives on their side, but there is a lack for selecting right image style in content context (like ckeditor, specific node, block). In Drupal we have many ways to map content item with right display settings, but it fails if image is remote and we don't need generate derivative ourselfs.

I will check this issue more deeply!

steinmb’s picture