Make WordPress Core

Opened 3 weeks ago

Last modified 3 weeks ago

#65656 assigned defect (bug)

Media: add an opt-in filter to generate animated image sub-sizes

Reported by: adamsilverstein Owned by: adamsilverstein
Priority: normal Milestone: 7.2
Component: Media Version:
Severity: normal Keywords: has-patch has-unit-tests gutenberg-merge
Cc: Focuses:

Description

Background

Resized versions of animated GIFs have always lost their animation: both GD and Imagick flatten animated images when resizing. This is the subject of #28474, open since 2014. It stalled server-side because GD cannot preserve frames at all, Imagick is only available to a subset of hosts, and per-frame re-encoding is costly on shared server CPU.

Client-side media processing (see #64804) changes that calculus. Image decoding, scaling, and encoding happen in the uploading user's browser via wasm-vips, which is available regardless of host configuration and can decode and re-encode every frame of an animated image. The cost is paid once, by the uploader, rather than on the server.

Animated re-encodes are still expensive (roughly 10-20 seconds for a very large GIF even with tuned encoder settings), so this should not be the default. It is proposed as a developer opt-in.

Proposal

Add a new filter, wp_generate_animated_image_subsizes (boolean, default false):

<?php
add_filter( 'wp_generate_animated_image_subsizes', '__return_true' );

The filtered value is exposed on the REST API index as animated_image_subsizes, inside the existing client-side media processing block in WP_REST_Server::get_index() (alongside image_sizes and image_size_threshold, only for users who can upload_files). The client-side pipeline reads it from there and, when enabled, keeps the animation in uncropped sub-sizes of animated GIFs instead of flattening them to the first frame.

Behavior details:

  • Only uncropped sub-sizes (medium, medium_large, large, uncropped theme sizes) keep their animation. Cropped sizes such as thumbnail are always generated from the first frame; per-frame smart-cropping is out of scope.
  • The filter name says "animated image" rather than "GIF" deliberately: the client pipeline can also preserve frames for animated WebP, and APNG has come up on #28474. The initial implementation is exercised with GIFs.
  • Server-side uploads still produce static sub-sizes; the filter currently only affects the client-side path. If server-side animated resizing (per #28474) ever lands for Imagick, the same filter can control it, which is why it is proposed in core rather than as an editor-only setting.
  • wp_calculate_image_srcset() already declines to mix the full-size animated GIF with its sub-sizes in one srcset. With animated sub-sizes that guard becomes overly conservative but harmless; relaxing it can be a follow-up.

Implementation

The client-side consumption (editor settings plumbing, upload pipeline, and the vips worker restoring the all-frames load path with tuned GIF encoder settings) ships via the Gutenberg package updates in Gutenberg PR #80385 (see Gutenberg issue #80383 for the full design discussion, including the encoder tuning that makes the opt-in cost reasonable).

The core-side change is small: apply the new filter in WP_REST_Server::get_index(), expose the value on the index, and add the field to the block editor preload field lists in wp-admin/edit-form-blocks.php and wp-admin/site-editor.php (these must stay in sync with packages/core-data/src/entities.js for preloading to work).

Core PR: https://github.com/WordPress/wordpress-develop/pull/12572

  • #28474 - WordPress destroys animation in animated GIF when it resizes (this provides an opt-in resolution for editor uploads via the client-side path, as proposed in comment 58)
  • #64804 - Client-side media processing

Change History (5)

#1 @adamsilverstein
3 weeks ago

  • Owner set to adamsilverstein
  • Status newassigned

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


3 weeks ago
#2

  • Keywords has-unit-tests added; needs-unit-tests removed

Core backport of the server-side changes in https://github.com/WordPress/gutenberg/pull/80385 (design discussion in https://github.com/WordPress/gutenberg/issues/80383).

Resized versions of animated GIFs have always lost their animation - the subject of the 12-year-old Trac #28474, which stalled server-side because GD cannot preserve frames and Imagick is only available on a subset of hosts. The client-side media processing path can decode and re-encode every frame in the uploading user's browser, independent of host configuration, making animation-preserving sub-sizes feasible as a developer opt-in (they remain too expensive to be the default).

This adds the new wp_generate_animated_image_subsizes filter (boolean, default false) and exposes its value on the REST API index as animated_image_subsizes, inside the existing client-side media processing block in WP_REST_Server::get_index() (alongside image_size_threshold, only for users who can upload_files). When a site opts in, the client pipeline keeps the animation in uncropped sub-sizes of animated GIFs; cropped sizes such as thumbnail always flatten to the first frame.

The field is also added to the block editor root-index preload field lists in wp-admin/edit-form-blocks.php and wp-admin/site-editor.php - these must match packages/core-data/src/entities.js exactly for preloading to work, and the matching entities.js change is part of the Gutenberg PR. (Note: #12508 may want to do the same for image_strip_meta / image_max_bit_depth.)

The client-side consumption (editor settings plumbing, @wordpress/upload-media, and the @wordpress/vips worker restoring the all-frames load path with tuned GIF encoder settings) ships via the Gutenberg package updates in https://github.com/WordPress/gutenberg/pull/80385.

---

## Proposed commit message

Media: Add an opt-in filter to generate animated image sub-sizes.

Sub-sizes of animated images have always been static first-frame images: GD and Imagick flatten animated images when resizing, and re-encoding every frame per sub-size is very resource intensive. The client-side media processing path can preserve animation in the uploading user's browser, independent of host Imagick availability.

Add a new wp_generate_animated_image_subsizes filter (boolean, default false) and expose it on the REST API index alongside the other client-side media processing settings, so the client pipeline can honor it for uncropped sub-sizes. The client-side consumption ships via the Gutenberg package updates in https://github.com/WordPress/gutenberg/pull/80385.

Fixes #65656. See #28474, #64804.

#3 @wildworks
3 weeks ago

  • Keywords gutenberg-merge added
  • Milestone Awaiting Review7.1

I would like to change the milestone because this feature might be included in the 7.1 release.

@adamsilverstein commented on PR #12572:


3 weeks ago
#4

Just noting we have decided to land this in 7.2.

#5 @adamsilverstein
3 weeks ago

  • Milestone 7.17.2

I would like to change the milestone because this feature might be included in the 7.1 release.

We have decided to move this to the 7.2 release, see discussion on https://github.com/WordPress/gutenberg/pull/80385. I have milestoned as such.

Note: See TracTickets for help on using tickets.