Skip to content

Fix #303: native WebP and AVIF support for image upload and processing - #317

Open
jeanmarcos-dev wants to merge 13 commits into
mage-os:release/4.xfrom
jeanmarcos-dev:feature/native-webp-support
Open

Fix #303: native WebP and AVIF support for image upload and processing#317
jeanmarcos-dev wants to merge 13 commits into
mage-os:release/4.xfrom
jeanmarcos-dev:feature/native-webp-support

Conversation

@jeanmarcos-dev

@jeanmarcos-dev jeanmarcos-dev commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Description (*)

Makes WebP and AVIF first-class input formats: they can be uploaded, validated, resized, browsed and served everywhere Magento already accepts an image. This is the reduced scope agreed in the discussion above — the format registry and the generated-copies feature are out, and the diff is down from 84 files to 52, from +2562/-154 to +357/-51.

WebP was blocked at two independent levels, and both had to be addressed for uploading to actually work:

The engine. Gd2::$_callbacks mapped only GIF/JPEG/PNG/XBM/WBMP, so open() raised "Unsupported image format" before any allow-list mattered. It now carries the WebP and AVIF entries. Four related fixes on the same path:

  • save() honours the configured quality. WebP previously fell through to default, so $quality stayed null and system/upload_configuration/jpeg_quality was ignored.
  • _getTransparency() assumed "transparency is supported by gif/png only", which flattened WebP alpha onto black. WebP and AVIF are always decoded into a truecolor resource, and detecting real alpha means parsing container-specific flags — getting that wrong is what produces the black background. Treating them as potentially-alpha routes them down the same path as a truecolor PNG, which is correct either way.
  • crop() only preserved alpha for PNG; it now covers WebP and AVIF too.
  • _getCallback() checks the GD helper exists. GD only declares imagewebp/imageavif when it was built against libwebp/libavif, so without this a build lacking the codec raised "Call to undefined function". It now surfaces as the InvalidArgumentException for an unsupported format, which the rest of the stack already handles.

ImageMagick::_applyOptions() forced COMPRESSION_JPEG unconditionally, which on WebP or AVIF output yields a container whose declared compression contradicts its payload. It is now applied only to the other formats, leaving Imagick's own default in place.

The allow-lists. jpg jpeg gif png was hardcoded in about twenty places: di.xml arrays, ui_component <allowedExtensions>, _getAllowedExtensions() overrides, MIME maps in Framework\File\Mime and Filesystem\Driver\File\Mime, Framework\Api\ImageProcessor and ImageContentValidator, Catalog's gallery processor and image attribute backend, MediaGalleryUi\Ui\Component\ImageUploader, the CatalogImportExport uploader, and the MediaGalleryRenditions filename pattern. Each gained webp and avif alongside the existing entries.

Two of them are worth calling out:

  • MediaStorage\Model\File\Validator\Image reads if (mime is in the list) { try to open it } and returns true otherwise. Adding WebP there adds real content validation rather than removing it — the opposite of how the diff reads at a glance.
  • The client-side lists mattered as much as the server ones. Four uploaders enforce their own list in JavaScript before any request is made, so until media-uploader.js, base-image-uploader.js, new-video-dialog.js and the configurable-product bulk.js were updated, the admin refused .webp while the server already accepted it.

Error reporting. The gallery upload controller only translated LocalizedException, so the image adapter's own message was replaced by "Something went wrong while saving the file(s)." — which is exactly what a perfectly valid AVIF gets on a build whose GD lacks libavif, now that the format is allowed through. It now surfaces those messages, so the merchant reads "Unsupported image format." and can act on it.

Caveats worth a reviewer's attention

  • AVIF decoding depends on how the build was compiled. GD needs libavif and Imagick needs the AVIF delegate; neither is universal yet. This is why the function_exists() guard and the error-reporting fix are part of this PR rather than a follow-up — without them an AVIF upload on a common build is a fatal error or an unactionable message. WebP is safe: libwebp has been standard in distro GD builds for years.
  • jpe is not included. The JPEG entries stay jpg/jpeg as they are today, so nothing widens beyond WebP and AVIF.

Out of scope

Two parts were split out during review, per the thread above: the format registry (opening separately) and generated WebP/AVIF copies of cached catalog images (dropped).

Beyond that, three paths deliberately keep their current behaviour:

  • WebP metadata. MediaGalleryMetadata implements each container separately — Model/Png is 974 lines, Model/Gif 736 — so WebP needs an equivalent Model/Webp parsing and rewriting the RIFF EXIF/XMP chunks: a binary-format implementation in a module this PR does not otherwise touch, comparable in size to the whole PR, and not something Native WebP support #303 asks for. The consequence is cosmetic and I verified both directions, since they handle errors differently: reading falls into File\ExtractMetadata's catch and yields empty metadata, and writing hits File\AddMetadata's ValidatorException branch and returns silently, because all three readers throw exactly that on a signature mismatch. So a .webp asset simply has no title, description or keywords imported from the file, and admin-entered ones live only in Magento's database. Worth its own PR.
  • The PDF print logo. Magento\Framework\Pdf cannot embed WebP, so Config\Backend\Image\Pdf keeps overriding the list with tif/tiff/pdf and still rejects it.
  • The favicon. Theme\Model\Design\Backend\Favicon keeps its own ico/png/gif/jpg/jpeg/apng list. Browser favicon support does not track <img> support, and .ico/.png remain the safe choices.

Related Pull Requests

The image format registry, as a follow-up to this PR.

Fixed Issues (if relevant)

  1. Fixes Native WebP support #303

Manual testing scenarios (*)

Verified on PHP 8.3 with GD (libwebp and libavif) and Imagick with both delegates, driving the real admin forms in a browser. The unsupported-build path was exercised too: on the same install before GD was rebuilt with libavif, an AVIF upload was refused with an unsupported-format error rather than a fatal.

  1. Upload a .webp to a product's Images and Videos gallery — it uploads, the thumbnail renders, the storefront shows it.
  2. Same for a category image, the Media Gallery, and the store logo.
  3. Import a product by CSV with a .webp base_image URL, and POST /V1/products/:sku/media with a base64 WebP.
  4. Upload a transparent .webp and confirm the cache images keep transparency instead of turning black.
  5. Confirm .webp is still rejected for the PDF print logo.
  6. Upload an .avif to a product gallery, with the adapter set to GD2 and again to IMAGEMAGICK.

Questions or comments

  1. The Images (...) file-picker labels. Backend\Block\Media\Uploader and the product gallery Content block each carry a translatable label listing the extensions, which now has to be kept in step with the files array beside it by hand. Both were already out of sync on main (the gallery label said .gif, .jpg, .png while its filter accepted .jpeg), so I extended them literally rather than deriving them. Happy to leave them as is or take another approach.
  2. Customer\Model\Metadata\Form\Image and Eav\Model\Attribute\Data\Image keyed their maps on the bare integers 1, 2, 3. Adding 18 and 19 for WebP and AVIF would have been inscrutable, so those five entries now use the IMAGETYPE_* constants. Same values, no behaviour change.

Contribution checklist (*)

  • Pull request has a meaningful description of its purpose
  • All commits are accompanied by meaningful commit messages
  • All new or changed code is covered with unit/integration tests (if applicable)
  • README.md files for modified modules are updated and included in the pull request if any README.md predefined sections require an update
  • All automated tests passed successfully (all builds are green) — verified locally against a reproduction of CI's setup (no setup:di:compile, one PHPUnit run per module as nx affected does). Every affected module is green: Catalog 2639, Customer 1401, View 810, Backend 583, Config 436, Theme 435, Eav 422, Cms 289, CatalogImportExport 243, ConfigurableProduct 242, Email 181, Filesystem 112, Swatches 110, MediaStorage 76, Api 74, ProductVideo 36, File 33, Image 25, MediaGalleryUi 17, MediaGalleryRenditions 6. Also setup:di:compile, and phpcs clean at the gate's own severity (--severity=5 --warning-severity=8 --error-severity=8), with no new warnings on any touched file. The integration and MFTF suites were run on the original combined branch, not re-run after the reduction, so I am deferring those to CI — the reduction only removes code, and the four integration tests that covered the dropped output-format API were removed with it. From those runs: AdminProductImageAvifUploadTest passed consistently, and AdminSimpleProductImagesTest is flaky in my environment — four runs with unmodified upstream JS failed in three different places, and it already carries pr_exclude for AC-12124 — but three of four runs reached and passed the new WebP step.

@jeanmarcos-dev
jeanmarcos-dev requested a review from a team as a code owner August 7, 2026 02:50
@rhoerr

rhoerr commented Aug 7, 2026

Copy link
Copy Markdown
Member

Initial thoughts:

  • I think it's doing too much (for one PR).
  • I think the copy generating aspect should be a separate PR at minimum. I'm not sold on it becoming part of core at all. Converting images to WebP is a nice benefit, but as noted, the impact on processing time and storage requirements can be substantial. This would be all new ground compared to Magento, and it's pretty well covered by other solutions too (WebP extensions, including open source; CDNs; image services).
  • I appreciate that the image format registry is a cleaner approach than a lot of hardcoded lists, and Magento should have done similar in the first place, but it didn't. Introducing this would be a compatibility issue and a source of never-ending merge conflicts. Image formats are also not likely to change frequently, so it doesn't seem worth the cost.

Setting aside the question of whether it's clean and proper in the big scheme, the solution I would like to see is adding the ability to upload WebP images with minimal changes to make it happen. Adding its mime type and extension to lists, adding resize + parsing support, etc.

Open to discussing it further.

@jeanmarcos-dev

Copy link
Copy Markdown
Contributor Author

Initial thoughts:

  • I think it's doing too much (for one PR).
  • I think the copy generating aspect should be a separate PR at minimum. I'm not sold on it becoming part of core at all. Converting images to WebP is a nice benefit, but as noted, the impact on processing time and storage requirements can be substantial. This would be all new ground compared to Magento, and it's pretty well covered by other solutions too (WebP extensions, including open source; CDNs; image services).
  • I appreciate that the image format registry is a cleaner approach than a lot of hardcoded lists, and Magento should have done similar in the first place, but it didn't. Introducing this would be a compatibility issue and a source of never-ending merge conflicts. Image formats are also not likely to change frequently, so it doesn't seem worth the cost.

Setting aside the question of whether it's clean and proper in the big scheme, the solution I would like to see is adding the ability to upload WebP images with minimal changes to make it happen. Adding its mime type and extension to lists, adding resize + parsing support, etc.

Open to discussing it further.

Thanks, this is fair feedback and I agree the PR is doing too much. Let me split it into three, ordered so the part you actually asked for lands on its own:

  1. WebP + AVIF as input formats — minimal changes. Exactly what you described: mime types and extensions added to the existing lists, plus the engine work needed to make it real.

  2. The format registry — separate PR, easy to say no to. Your compatibility and merge-conflict concerns are the right ones and I don't have a counterargument that outweighs them; a registry only pays off if formats change, and you're right that they don't. I'll open it standalone so it can be judged on its own merits, and I'd rather it be rejected there than hold up (1).

  3. Generated copies — only if there's appetite. Agreed this is new ground for Magento and well served by CDNs, image services and existing extensions. I measured it on my install, and the numbers support your reservation: catalog:images:resize goes +12% for WebP and +85% for WebP+AVIF, and an uncached image through get.php goes +25% / +160% — which matters because category listings generate on request, not at render. I'll leave it out and only propose it later if there's interest.

Happy to reduce this PR down to scope (1) with a force-push so the discussion and the issue link stay in one place, or close it and open a fresh one — whichever you prefer.

@rhoerr

rhoerr commented Aug 7, 2026

Copy link
Copy Markdown
Member

Force push is fine -- thanks

@jeanmarcos-dev
jeanmarcos-dev force-pushed the feature/native-webp-support branch from f096e57 to e390eb3 Compare August 7, 2026 15:19
@jeanmarcos-dev jeanmarcos-dev changed the title Fix #303: native WebP and AVIF support, with optional generated copies Fix #303: native WebP and AVIF support for image upload and processing Aug 7, 2026
@jeanmarcos-dev

Copy link
Copy Markdown
Contributor Author

Force push is fine -- thanks

This PR is now scope (1) only: WebP and AVIF as input formats.

@rhoerr

rhoerr commented Aug 7, 2026

Copy link
Copy Markdown
Member

Tentatively, this looks good to me now. Still need to QA and check for completeness. Thank you @jeanmarcos-dev

@rhoerr
rhoerr changed the base branch from main to release/4.x August 7, 2026 18:22
@rhoerr rhoerr added the enhancement New feature or request label Aug 7, 2026
@rhoerr

rhoerr commented Aug 8, 2026

Copy link
Copy Markdown
Member

Please check:

category image upload still rejects WebP server-side

app/code/Magento/Catalog/etc/di.xml:226-245 — the Magento\Catalog\CategoryImageUpload virtualType was not touched:

<argument name="allowedExtensions" xsi:type="array">  jpg jpeg gif png  </argument>
<argument name="allowedMimeTypes" xsi:type="array">   image/jpg … image/png  </argument>

category_form.xml:184 now advertises jpg jpeg gif png webp avif, but the POST goes to catalog/category_image/upload → Catalog/Controller/Adminhtml/Category/Image/Upload.php:56 → ImageUploader::saveFileToTmpDir(), which does setAllowedExtensions($this->getAllowedExtensions()) and checkMimeType($this->allowedMimeTypes) (Catalog/Model/ImageUploader.php:246-249). A .webp gets "File validation failed."
  • Customer/Controller/Adminhtml/Index/Viewfile.php:157-168 and Address/Viewfile.php:117-128 — the content-type switch maps only gif/jpg/png; webp/avif fall through to application/octet-stream, so admin preview of a customer image attribute downloads instead of rendering. This is in scope because the PR enabled WebP in Customer\Model\Metadata\Form\Image and Eav\Model\Attribute\Data\Image. (jpeg is missing there upstream too.)
  • i18n dictionaries not updated. Backend/i18n/en_US.csv:28 and Catalog/i18n/en_US.csv:157 still carry "Images (.gif, .jpg, .png)"; both __() call sites changed, orphaning the entries.
  • Deploy/Console/InputValidator.php:25-50 — no webp/avif in $fileExtensionOptionMap. DeployPackage.php:241 maps an unknown extension to $option = null → never excluded, so --no-images silently keeps WebP/AVIF theme assets. One line, low severity.
  • Sales/etc/adminhtml/system.xml:80 — the HTML-logo comment still says "(jpeg, gif, png)" although Config\Backend\Image\Logo now accepts webp/avif.

Worth a second look on the deliberate exclusions

Favicon and the PDF logo were excluded, correctly. But the email/HTML logo (Email\Model\Design\Backend\Logo, Config\Backend\Image\Logo) now accepts WebP/AVIF, and Outlook desktop and several mail clients render neither. That's the same class of problem the favicon exclusion is guarding against — either exclude it too or put a warning in the design_config_form notice.

Verified as already covered

nginx.conf.sample and pub/{media,static}/.htaccess already ship webp/avif upstream; Filesystem\Driver\File\Mime (native mime_content_type); WYSIWYG and the standalone Media Gallery upload (both route through Cms/etc/di.xml); theme file browser (Backend\Block\Media\Uploader defaults + Theme\Helper\Storage); watermark and product placeholder (Config\Backend\Image); Gd2::_getImageNeedMemorySize() tolerates WebP's missing channels/bits; ProductVideo remote-image retrieval uses NotProtectedExtension; import/export and REST/GraphQL base64 (ImageContentValidator, ImageProcessor, MimeTypeExtensionMap); renditions; and MediaStorage\Service\ImageResize / product image cache have no extension filters at all.

@jeanmarcos-dev
jeanmarcos-dev force-pushed the feature/native-webp-support branch from 97b3a92 to 2ba5514 Compare August 8, 2026 22:27
@jeanmarcos-dev

Copy link
Copy Markdown
Contributor Author

Thanks for the review — all points are addressed.

  • Customer file viewerAdminhtml\Index\Viewfile and Adminhtml\Address\Viewfile fell through to application/octet-stream, so an uploaded .webp downloaded instead of rendering. Both now map the new types, with unit coverage.
  • Static content deployDeploy\Console\InputValidator now covers webp/avif in the --no-images filter.
  • Category uploader — added to the CategoryImageUpload virtual type, with an integration test that drives the real uploader.
  • Email logo — reverted, you were right. That logo is embedded in email HTML, where Outlook desktop renders neither format and there is no <picture> fallback. It keeps jpg jpeg gif png.
  • Admin labels — the Images (...) pickers and the watermark / HTML-logo hints now list the new formats.

I also rebased onto release/4.x after you moved the base. The branch had been cut from main, so the diff was carrying 16 files of your own recent work (the Redis adapters and the PHP 8.5 fixes). It is 67 files now, all of them mine.

One change worth flagging: UploadTest@upload_empty_image now expects "Wrong file size." instead of the generic message. That is the point of the error-reporting fix — validateUploadFile() throws three distinct, merchant-readable messages that all collapsed into the generic one before. The generic branch is still covered by upload_without_image and upload_wrong_png, and a new unit test pins all four branches of the controller.

@rhoerr

rhoerr commented Aug 11, 2026

Copy link
Copy Markdown
Member

Thanks. New code looks good. Will need to check it against the list. Still needs QA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Native WebP support

2 participants