Note on authorship: Drafted with Claude (Anthropic) after tracing the WebP code path in ImageSizerEngineGD. I have not yet patched and re-tested on my own install — flagging this so it can be evaluated/repro'd properly before merging. Cross-referenced against the canonical PHP-GD answer at https://stackoverflow.com/questions/67587861.
Affected versions: Observed against 3.0.262. The imSaveWebP() method has been structurally unchanged for several versions, so this likely affects every release that supports the webpAdd / webpOnly image options when the GD engine is in use.
Affected engine: ImageSizerEngineGD only. IMagick engine handles alpha correctly on its own.
Symptom
PNG source images with transparency get their alpha channel flattened to black (or to whatever background color the GD resource happens to carry) when the WebP variant is generated via image options like ["webpAdd" => true] or ["webpOnly" => true]. The original PNG output keeps transparency; only the sibling .webp file is broken.
Code location
wire/core/Image/ImageSizerEngineGD.php, lines 456–462:
protected function imSaveWebP($im, $filename, $quality = 90) {
if(!function_exists('imagewebp')) return false;
$path_parts = pathinfo($filename);
$webpFilename = $path_parts['dirname'] . '/' . $path_parts['filename'] . '.webp';
if(file_exists($webpFilename)) $this->wire()->files->unlink($webpFilename);
return imagewebp($im, $webpFilename, $quality);
}
Root cause
imagewebp() in PHP-GD requires the source resource to (a) be true-color and (b) have imagesavealpha(true) set at save time, otherwise the alpha channel is discarded. imSaveWebP() calls imagewebp() directly without ensuring either.
prepareImageLayer() (line 855) does set the right flags for PNG sources on the initial destination resource:
if($this->imageType == IMAGETYPE_PNG) {
imagealphablending($im, false);
imagesavealpha($im, true);
}
…but several downstream operations in this engine create new GD resources that don't inherit those flags. imRotate() (imagerotate), imFlip() (imagecreatetruecolor + imagecopyresampled), and sharpen/convolution paths can all hand imSaveWebP() a resource where savealpha is no longer true. When the original PNG passes through any of those transforms, the WebP variant comes out without transparency.
Proposed fix
Defensive flag-setting inside imSaveWebP() itself — covers every code path that lands here, regardless of upstream transforms:
protected function imSaveWebP($im, $filename, $quality = 90) {
if(!function_exists('imagewebp')) return false;
$path_parts = pathinfo($filename);
$webpFilename = $path_parts['dirname'] . '/' . $path_parts['filename'] . '.webp';
if(file_exists($webpFilename)) $this->wire()->files->unlink($webpFilename);
// Ensure the resource is in a state imagewebp() can preserve alpha from.
// imagewebp() silently drops the alpha channel if these aren't set, which
// is the cause of PNG-transparency loss on WebP variants.
if(function_exists('imagepalettetotruecolor')) imagepalettetotruecolor($im);
imagealphablending($im, false);
imagesavealpha($im, true);
return imagewebp($im, $webpFilename, $quality);
}
This mirrors the canonical PHP-GD pattern for transparency-safe WebP writes and matches what imSavePNG-style paths already rely on for PNG output.
Note: imSaveWebP() is not currently hookable (no ___ prefix), so users cannot work around this from userland without either editing core or switching to the IMagick engine.
Not verified yet
I have not applied this patch on my own install and re-rendered images to confirm the WebP now has alpha — happy to do so if helpful, but wanted to file the analysis first so it isn't lost.
Environment
- ProcessWire 3.0.262
- PHP 7.4
- GD engine (default)
- PNG-24 source images with alpha channel
Affected versions: Observed against 3.0.262. The
imSaveWebP()method has been structurally unchanged for several versions, so this likely affects every release that supports thewebpAdd/webpOnlyimage options when the GD engine is in use.Affected engine:
ImageSizerEngineGDonly. IMagick engine handles alpha correctly on its own.Symptom
PNG source images with transparency get their alpha channel flattened to black (or to whatever background color the GD resource happens to carry) when the WebP variant is generated via image options like
["webpAdd" => true]or["webpOnly" => true]. The original PNG output keeps transparency; only the sibling.webpfile is broken.Code location
wire/core/Image/ImageSizerEngineGD.php, lines 456–462:Root cause
imagewebp()in PHP-GD requires the source resource to (a) be true-color and (b) haveimagesavealpha(true)set at save time, otherwise the alpha channel is discarded.imSaveWebP()callsimagewebp()directly without ensuring either.prepareImageLayer()(line 855) does set the right flags for PNG sources on the initial destination resource:…but several downstream operations in this engine create new GD resources that don't inherit those flags.
imRotate()(imagerotate),imFlip()(imagecreatetruecolor+imagecopyresampled), and sharpen/convolution paths can all handimSaveWebP()a resource wheresavealphais no longer true. When the original PNG passes through any of those transforms, the WebP variant comes out without transparency.Proposed fix
Defensive flag-setting inside
imSaveWebP()itself — covers every code path that lands here, regardless of upstream transforms:This mirrors the canonical PHP-GD pattern for transparency-safe WebP writes and matches what
imSavePNG-style paths already rely on for PNG output.Note:
imSaveWebP()is not currently hookable (no___prefix), so users cannot work around this from userland without either editing core or switching to the IMagick engine.Not verified yet
I have not applied this patch on my own install and re-rendered images to confirm the WebP now has alpha — happy to do so if helpful, but wanted to file the analysis first so it isn't lost.
Environment