Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DOC Document upgrading intervention/image #547

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 73 additions & 4 deletions en/02_Developer_Guides/14_Files/02_Images.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,61 @@ Please refer to the [`ImageManipulation`](api:SilverStripe\Assets\ImageManipulat

See [file manipulation](./file_manipulation/) for more general ways that all files can be manipulated.

### Animated images

Manipulating animated images takes longer, and results in a larger filesize.

Because of this, the [`ThumbnailGenerator`](api:SilverStripe\AssetAdmin\Model\ThumbnailGenerator) will provide still images as thumbnails for animated gifs by default. You can change that for a given instance of `ThumbnailGenerator` by passing `true` to the [`setAllowsAnimation()`](api:SilverStripe\AssetAdmin\Model\ThumbnailGenerator::setAllowsAnimation()) method. For example, to allow animated thumbnails for `UploadField`:

```yml
---
After: '#assetadminthumbnails'
---
SilverStripe\Core\Injector\Injector:
SilverStripe\AssetAdmin\Model\ThumbnailGenerator.assetadmin:
properties:
AllowsAnimation: true
```

The [`Image::PreviewLink()`](api:SilverStripe\Assets\Image::PreviewLink()) method also doesn't allow an animated result by default. This is used in the "Files" admin section, and anywhere you can choose an existing image such as `UploadField` and the WYSIWYG file modals.

You can allow animated previews by setting [`Image.allow_animated_preview`](api:SilverStripe\Assets\Image->allow_animated_preview) configuration property to `true`:

```yml
SilverStripe\Assets\Image:
allow_animated_preview: true
```

You can disable the ability to create animated variants globally by setting `decodeAnimation` to `false` in the `Intervention\Image\ImageManager`'s constructor:

```yml
SilverStripe\Core\Injector\Injector:
Intervention\Image\ImageManager:
constructor:
decodeAnimation: false
```

This affects *all* scenarios where variants are created, both on the front-end and in the backend.

> [!TIP]
> Disabling animation globally is more performant than using `RemoveAnimation()` as described below, because it tells `intervention/image` to always only look at the first frame of any animated image - it doesn't even decode the remaining frames.

When manipulating images yourself in templates, you can use the new [`RemoveAnimation()`](api:SilverStripe\Assets\ImageManipulation::RemoveAnimation()) method before your resizing methods:

```ss
$MyImage.RemoveAnimation.FitMax(300, 200)
```

`RemoveAnimation()` takes an optional parameter which you can use to determine which frame of animation is used as the still image. You can either pass in an exact frame number (0-indexed), or a percentage as a string (e.g. `$MyImage.RemoveAnimation('50%')` will use a frame halfway through the animation).

If the image isn't animated `RemoveAnimation()` will just return the original image without generating a variant, so it's safe to use without first checking if the image is animated.

You can also use the [`setAllowsAnimationInManipulations()`](api:SilverStripe\Assets\Image_Backend::setAllowsAnimationInManipulations()) method to toggle the `decodeAnimation` configuration setting for a given image. This is useful if you intend to make many manipulations to an image, and you want some to include animation and others to not include animation - you can simply toggle animation usage on and off using this method.

```php
$myImage->getImageBackend()->setAllowsAnimationInManipulations(false);
```

### Creating custom image functions

You can also create your own functions by decorating the `Image` class.
Expand Down Expand Up @@ -318,18 +373,32 @@ SilverStripe\Assets\Image:
lazy_loading_enabled: false
```

## Changing the manipulation driver to imagick
## Changing the manipulation driver {#intervention-image-driver}

If you have the [imagick PHP extension](https://www.php.net/manual/en/book.imagick.php) installed, it will be used as the driver for `intervention/image` by default. If you don't, the assumption is that you have the [GD PHP extension](https://www.php.net/manual/en/book.image.php) installed, and it will be used instead.

If you want to change the image manipulation driver to use Imagick instead of GD, you'll need to change your config so
that the `Intervention\Image\ImageManager` is instantiated with the `imagick` driver instead of GD:
If you want to change the image manipulation driver to something else (either a third-party driver, or else use GD even when imagick is installed), you need to configure that via the injector:

```yml
---
After: '#assetsimage-imagick'
---
SilverStripe\Core\Injector\Injector:
InterventionImageDriver:
class: 'Intervention\Image\Drivers\Gd\Driver'
```

You can also set various configuration options for the driver to use by setting them in the constructor for `Intervention\Image\ImageManager`, for example:

```yml
SilverStripe\Core\Injector\Injector:
Intervention\Image\ImageManager:
constructor:
- { driver: imagick }
decodeAnimation: false
```

The options available are detailed in the [intervention/image documentation](https://image.intervention.io/v3/basics/image-manager#configuration-options).

## Storage

Manipulated images are stored as "file variants" in the same folder structure as the original image. The storage mechanism is described in the ["File Storage" guide](file_storage).
Expand Down
8 changes: 4 additions & 4 deletions en/02_Developer_Guides/14_Files/05_File_Manipulation.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ This can be very useful if you want to convert a file to a different format for

#### Making our own `FileConverter`

Converting between image formats is the easiest example, because we can let [Intervention Image](https://image.intervention.io/v2) do the heavy lifting for us. Note that there is a built in [`InterventionImageFileConverter`](api:SilverStripe\Assets\Conversion\InterventionImageFileConverter) class which does this already, but we'll use this as an example for how to create our own `FileConverter`.
Converting between image formats is the easiest example, because we can let [Intervention Image](https://image.intervention.io/v3) do the heavy lifting for us. Note that there is a built in [`InterventionImageFileConverter`](api:SilverStripe\Assets\Conversion\InterventionImageFileConverter) class which does this already, but we'll use this as an example for how to create our own `FileConverter`.

The `FileConverter` interface requires us to implement two methods:

Expand All @@ -219,7 +219,7 @@ The `FileConverter` interface requires us to implement two methods:
```php
namespace App\Conversion;

use Intervention\Image\Exception\ImageException;
use Intervention\Image\Exceptions\RuntimeException;
use SilverStripe\Assets\Conversion\FileConverter;
use SilverStripe\Assets\Conversion\FileConverterException;
use SilverStripe\Assets\File;
Expand Down Expand Up @@ -248,7 +248,7 @@ class ImageFileConverter implements FileConverter
return [$tuple, $backend];
}
);
} catch (ImageException $e) {
} catch (RuntimeException $e) {
throw new FileConverterException('Failed to convert: ' . $e->getMessage(), $e->getCode(), $e);
}
}
Expand Down Expand Up @@ -320,7 +320,7 @@ Our callback returns both the information about the variant file and the `Image_
```php
try {
// ...
} catch (ImageException $e) {
} catch (RuntimeException $e) {
throw new FileConverterException('Failed to convert: ' . $e->getMessage(), $e->getCode(), $e);
}
```
Expand Down
67 changes: 67 additions & 0 deletions en/08_Changelogs/6.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ title: 6.0.0 (unreleased)
- [Run `CanonicalURLMiddleware` in all environments by default](#url-middleware)
- [Changes to scaffolded form fields](#scaffolded-fields)
- [Other new features](#other-new-features)
- [Dependency changes](#dependency-changes)
- [`intervention/image` has been upgraded from v2 to v3](#intervention-image)
- [Bug fixes](#bug-fixes)
- [API changes](#api-changes)
- [Most extension hook methods are now protected](#hooks-protected)
Expand Down Expand Up @@ -58,6 +60,71 @@ All other models used `SearchableDropdownField` for `has_one` relations and `Gri
- Native indexed PHP arrays can now be passed into templates and iterated over with `<% loop $MyArray %>`. Under the hood they are wrapped in [`ArrayList`](api:SilverStripe\View\ViewableData), so you can get the count using `$Count` and use `<% if $ArrayList %>` as a shortcut for `<% if $ArrayList.Count %>`. Other functionality from `ArrayList` such as filtering and sorting cannot be used on arrays since they don't have keys to filter or sort against.
- Modules no longer need to have a root level `_config.php` or `_config` directory to be recognised as a Silverstripe CMS module. They will now be recognised as a module if they have a `composer.json` file with a `type` of `silverstripe-vendormodule` or `silverstripe-theme`.

## Dependency changes

### `intervention/image` has been upgraded from v2 to v3 {#intervention-image}

We've upgraded from `intervention/image` v2 to v3. One of the main improvements included in this upgrade is full support for animated GIFs.

If you are directly interacting with APIs from `intervention/image` in your project or module you should check out [their upgrade guide](https://image.intervention.io/v3/introduction/upgrade).

#### Animated vs still images {#intervention-image-animations}

Manipulating animated images takes longer, and results in a larger filesize.

Because of this, the [`ThumbnailGenerator`](api:SilverStripe\AssetAdmin\Model\ThumbnailGenerator) will provide still images as thumbnails for animated gifs by default. You can change that for a given instance of `ThumbnailGenerator` by passing `true` to the [`setAllowsAnimation()`](api:SilverStripe\AssetAdmin\Model\ThumbnailGenerator::setAllowsAnimation()) method. For example, to allow animated thumbnails for `UploadField`:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also applies to all other PRs to changelogs - we're essentially duplicating docs on doc.silverstripe.org. We should just link to those docs instead of duplicating them in the changelog.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not at all uncommon for us to do this - we often give the most pertinent information directly in the changelog, even if it's also documented elsewhere.

This also applies to all other PRs to changelogs

I assume you want some amount of this removed, can you please indicate how much so I know how much I want to push back on it? :p

Copy link
Member

@emteknetnz emteknetnz Jul 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not at all uncommon for us to do this - we often give the most pertinent information directly in the changelog, even if it's also documented elsewhere.

Yeah we do ... though just looking at this PR I actually have no idea why we've been doing this. We're quick to call out duplicate documentation in all other scenarios and say "You should just link to the original source" ... I'm not sure why the changelog should be treated any differently.

I assume you want some amount of this removed, can you please indicate how much so I know how much I want to push back on it? :p

Probably everything not in the first section intervention/image has been upgraded from v2 to v3 {#intervention-image} - I'd keep that and say that imagick is now the default if it's available and link to the docs

Most of the rest is duplication of "how to configure it", which is basically straight duplication

I'd also remove the "New API" bit, it just seems unnecessary. Very few people are going to be interested in calling the PHP methods directly, the configuration is much more relevant. If they really are interested in calling PHP methods directly then there's typehinting available anyway.


```yml
---
After: '#assetadminthumbnails'
---
SilverStripe\Core\Injector\Injector:
SilverStripe\AssetAdmin\Model\ThumbnailGenerator.assetadmin:
properties:
AllowsAnimation: true
```

The [`Image::PreviewLink()`](api:SilverStripe\Assets\Image::PreviewLink()) method also doesn't allow an animated result by default. This is used in the "Files" admin section, and anywhere you can choose an existing image such as `UploadField` and the WYSIWYG file modals.

You can allow animated previews by setting [`Image.allow_animated_preview`](api:SilverStripe\Assets\Image->allow_animated_preview) configuration property to `true`:

```yml
SilverStripe\Assets\Image:
allow_animated_preview: true
```

You can disable the ability to create animated variants globally by setting `decodeAnimation` to `false` in the `Intervention\Image\ImageManager`'s constructor:

```yml
SilverStripe\Core\Injector\Injector:
Intervention\Image\ImageManager:
constructor:
decodeAnimation: false
```

You can also toggle that configuration setting on and off for a given image instance, or create a variant from your image which uses a specific frame of animation - see [animated images](/developer_guides/files/images/#animated-images) for details.

#### Using GD or Imagick {#intervention-image-driver}

One of the changes that comes as a result of this upgrade is a change in how you configure which manipulation driver (GD or Imagick) to use.

To facilitate upgrades and to ensure we are providing optimal defaults out of the box, if you have the [imagick PHP extension](https://www.php.net/manual/en/book.imagick.php) installed, it will be used as the driver for `intervention/image` by default. If you don't, the assumption is that you have the [GD PHP extension](https://www.php.net/manual/en/book.image.php) installed, and it will be used instead.

See [changing the manipulation driver](/developer_guides/files/images/#intervention-image-driver) for the new configuration for swapping the driver used by `intervention/image`.

#### New API {#intervention-image-new-api}

The following new methods have been added to facilitate this upgrade:

|Method name|Where the method was added|
|---|---|
|`getIsAnimated()`|[`AssetContainer::getIsAnimated()`](api:SilverStripe\Assets\Storage\AssetContainer::getIsAnimated()), [`ImageManipulation::getIsAnimated()`](api:SilverStripe\Assets\ImageManipulation::getIsAnimated()) (and therefore `DBFile`, `File`, and their subclasses), [`Image_Backend::getIsAnimated()`](api:SilverStripe\Assets\Image_Backend::getIsAnimated()), [`InterventionBackend::getIsAnimated()`](api:SilverStripe\Assets\InterventionBackend::getIsAnimated())|
|`RemoveAnimation()`|[`ImageManipulation::RemoveAnimation()`](api:SilverStripe\Assets\ImageManipulation::RemoveAnimation()) (and therefore `DBFile`, `File`, and their subclasses), [`Image_Backend::removeAnimation()`](api:SilverStripe\Assets\Image_Backend::removeAnimation()), [`InterventionBackend::removeAnimation()`](api:SilverStripe\Assets\InterventionBackend::removeAnimation())|
|`getAllowsAnimationInManipulations()`|[`Image_Backend::getAllowsAnimationInManipulations()`](api:SilverStripe\Assets\Image_Backend::getAllowsAnimationInManipulations()), [`InterventionBackend::getAllowsAnimationInManipulations()`](api:SilverStripe\Assets\InterventionBackend::getAllowsAnimationInManipulations())|
|`setAllowsAnimationInManipulations()`|[`Image_Backend::setAllowsAnimationInManipulations()`](api:SilverStripe\Assets\Image_Backend::setAllowsAnimationInManipulations()), [`InterventionBackend::setAllowsAnimationInManipulations()`](api:SilverStripe\Assets\InterventionBackend::setAllowsAnimationInManipulations())|
|`getAllowsAnimation()`|[`ThumbnailGenerator::getAllowsAnimation()`](api:SilverStripe\AssetAdmin\Model\ThumbnailGenerator::getAllowsAnimation())|
|`setAllowsAnimation()`|[`ThumbnailGenerator::setAllowsAnimation()`](api:SilverStripe\AssetAdmin\Model\ThumbnailGenerator::setAllowsAnimation())|

## Bug fixes

This release includes a number of bug fixes to improve a broad range of areas. Check the change logs for full details of these fixes split by module. Thank you to the community members that helped contribute these fixes as part of the release!
Expand Down
Loading