image icon indicating copy to clipboard operation
image copied to clipboard

White colour made alpha color of PNG on scale

Open SCIF opened this issue 9 months ago • 10 comments

Describe the bug

Looks like white background getting transformed to «transparent»

Code Example

Repro: https://github.com/SCIF/repro-intervention

make test

Inspect images/ folder. There are two options of the same file as input: having no alpha channel and having the alpha channel.

Expected behavior

White should not be transformed to alpha colour on scaling

Environment (please complete the following information):

  • PHP Version: 8.3
  • OS: Docker/Debian
  • Intervention Image Version: 3.11.2
  • GD, Imagick or libvips: 3.8.0RC2

SCIF avatar Apr 09 '25 04:04 SCIF

To me it looks similar to https://github.com/Intervention/image/issues/1426

SCIF avatar Apr 09 '25 04:04 SCIF

Thanks for reporting. In contrast to the image in #1426, neither of your versions seems to contain alpha transparency.

olivervogel avatar Apr 09 '25 14:04 olivervogel

Closed due to inactivity.

olivervogel avatar Apr 20 '25 06:04 olivervogel

@olivervogel I'd like to reopen this, I see what probably is the same error. Just in my case, I'm loading a JPEG, scaling it down and saving it as PNG. I get white parts of the image replaced with varying levels of transparency.

The original repro works for me (output files below):

Image Image

Note that @SCIF mentioned the "-alpha.png" image having alpha channel, not transparency per se. Given that the same happens to me with JPEG as a source, I suppose the problem might be something along the lines of alpha being handled incorrectly for non-aplha images.

From my debugging so far:

  • The issue seems to be ImageMagick specific (tested with GD, doesn't happen; don't have libvips on hand).
  • It doesn't seem to be an ImageMagick bug – using the same environment with intervention/image 2.5 gives me the correct result (white remains white).

vojtagrec avatar Sep 02 '25 09:09 vojtagrec

~Tried bisecting the issue and it's there even with intervention/image 3.0. So it's not a recent regression.~

I stand corrected, I had a flaw in my bisect. It looks like the issue was introduced in 3.8.0.

vojtagrec avatar Sep 02 '25 10:09 vojtagrec

Adding the following to Intervention\Image\Drivers\Imagick\Encoders\PngEncoder::encode() seems to fix the issue.

        if (! $output->getImageAlphaChannel()) {
            $output->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE);
        }

It looks like there is some (random?) alpha channel even wnile IMagick's getImageAlphaChannel() returns false?

vojtagrec avatar Sep 02 '25 13:09 vojtagrec

I'm not so sure about the "fix" anymore, in another environment only ALPHACHANNEL_RESET worked. Given how Imagick constants depend on ImageMagick version, it would be better to avoid that.

So far the best course of action seems to be "rolling back" the change from 3.8.0 conditionally (= if $output->getImageAlphaChannel() keep using PNG32, else use PNG). IMHO there is no benefit in using PNG32 for image without alpha channel.

vojtagrec avatar Sep 04 '25 12:09 vojtagrec

This might be related (so it might be ImageMagick 6.9 specific): https://github.com/ImageMagick/ImageMagick/discussions/3270

(Both the repro provided by @SCIF and my test environment use ImageMagick 6.9.)

vojtagrec avatar Sep 04 '25 13:09 vojtagrec

I can also see errors in the output image here. However, this does not seem to be directly related to Intervention Image.

I have reproduced with Imagick only code what happens under the hood at Intervention Image (see below). Errors also occur here. It should be noted that this apparently only happens with Imagemagick 6 and no longer occurs with 7. Furthermore, the errors do not always occur, but only in some runs, which I cannot explain.

<?php

declare(strict_types=1);

require_once './vendor/autoload.php';

$imageManager = \Intervention\Image\ImageManager::imagick();

foreach (['', '-alpha'] as $suffix) {
    // intervention version
    $file = $imageManager->read("/app/images/sample{$suffix}.png");
    $file->scale(height: 75);
    $file->toPng()->save("/app/images/output{$suffix}.png");

    // imagick only version
    $imagick = new Imagick();
    $imagick->readImage("/app/images/sample{$suffix}.png");
    $imagick = $imagick->coalesceImages();
    $imagick->setImageOrientation(Imagick::ORIENTATION_TOPLEFT);

    foreach ($imagick as $frame) {
        $frame->scaleImage(112, 75);
    }

    $imagick->setFormat('PNG32');
    $imagick->setImageFormat('PNG32');
    $imagick->setCompression(Imagick::COMPRESSION_ZIP);
    $imagick->setImageCompression(Imagick::COMPRESSION_ZIP);

    file_put_contents(
        "/app/images/imagick-output{$suffix}.png",
        $imagick->getImagesBlob(),
    );
}

echo var_dump(Imagick::getVersion());
echo "Done.\n";

I would pass this on to Imagemagick or Imagick.

olivervogel avatar Sep 06 '25 08:09 olivervogel

@olivervogel Thanks for your time. We'll upgrade to ImageMagick 7 or libvips eventually (we can't do it right away though), so we'll just patch intervention/image in the meantime (to basically return to the behaviour before 3.8.0).

For anyone interested, we'll be using cweagans/composer-patches to apply the following patch:

--- /dev/null
+++ ../src/Drivers/Imagick/Encoders/PngEncoder.php
@@ -26,13 +26,12 @@
             $output->reduceColors(256);
 
             $output = $output->core()->native();
-            $output->setFormat('PNG');
-            $output->setImageFormat('PNG');
         } else {
             $output = clone $image->core()->native();
-            $output->setFormat('PNG32');
-            $output->setImageFormat('PNG32');
         }
+
+        $output->setFormat('PNG');
+        $output->setImageFormat('PNG');
 
         $output->setCompression(Imagick::COMPRESSION_ZIP);
         $output->setImageCompression(Imagick::COMPRESSION_ZIP);

Caution: it will change the output file format in some cases (e.g. turn images into indexed where the source was not). Always test this for your use case (it's OK for ours). The main point is that it doesn't seem to corrupt images.

vojtagrec avatar Sep 10 '25 09:09 vojtagrec