@@ -444,6 +444,53 @@ protected function thumbnail_image( $dst_w, $dst_h, $filter_name = 'FILTER_TRIAN
444444 }
445445
446446 try {
447+ /*
448+ * We need to perform some special handling for certain types of images:
449+ * 1. For PNG images, we need to specify compression settings and remove unneeded chunks.
450+ * 2. For indexed PNG images, the number of colors must not exceed 256.
451+ * 3. For indexed PNG images with an alpha channel, the tRNS chunk must be preserved.
452+ * 4. For indexed PNG images with true alpha transparency (an alpha channel > 1 bit),
453+ * we need to avoid saving the image using ImageMagick's 'png8' format,
454+ * because that supports only binary (1 bit) transparency.
455+ *
456+ * For #4 we want to check whether the image has a 1-bit alpha channel before resizing,
457+ * because resizing may cause the number of alpha values to multiply due to antialiasing.
458+ * (We're assuming that, if the original image had only a 1-bit alpha channel,
459+ * then a 1-bit alpha channel should be good enough for the resized images too.)
460+ * So we're going to perform all the necessary checks before resizing the image
461+ * and store the results in variables for later use.
462+ */
463+ $ is_png = false ;
464+ $ is_indexed_png = false ;
465+ $ is_indexed_png_with_alpha_channel = false ;
466+ $ is_indexed_png_with_true_alpha_transparency = false ;
467+
468+ if ( 'image/png ' === $ this ->mime_type ) {
469+ $ is_png = true ;
470+
471+ if (
472+ is_callable ( array ( $ this ->image , 'getImageProperty ' ) )
473+ && '3 ' === $ this ->image ->getImageProperty ( 'png:IHDR.color-type-orig ' )
474+ ) {
475+ $ is_indexed_png = true ;
476+
477+ if (
478+ is_callable ( array ( $ this ->image , 'getImageAlphaChannel ' ) )
479+ && $ this ->image ->getImageAlphaChannel ()
480+ ) {
481+ $ is_indexed_png_with_alpha_channel = true ;
482+
483+ if (
484+ is_callable ( array ( $ this ->image , 'getImageChannelDepth ' ) )
485+ && defined ( 'Imagick::CHANNEL_ALPHA ' )
486+ && 1 < $ this ->image ->getImageChannelDepth ( Imagick::CHANNEL_ALPHA )
487+ ) {
488+ $ is_indexed_png_with_true_alpha_transparency = true ;
489+ }
490+ }
491+ }
492+ }
493+
447494 /*
448495 * To be more efficient, resample large images to 5x the destination size before resizing
449496 * whenever the output size is less that 1/3 of the original image size (1/3^2 ~= .111),
@@ -480,30 +527,42 @@ protected function thumbnail_image( $dst_w, $dst_h, $filter_name = 'FILTER_TRIAN
480527 $ this ->image ->setOption ( 'jpeg:fancy-upsampling ' , 'off ' );
481528 }
482529
483- if ( ' image/png ' === $ this -> mime_type ) {
530+ if ( $ is_png ) {
484531 $ this ->image ->setOption ( 'png:compression-filter ' , '5 ' );
485532 $ this ->image ->setOption ( 'png:compression-level ' , '9 ' );
486533 $ this ->image ->setOption ( 'png:compression-strategy ' , '1 ' );
487534
488535 // Indexed PNG files get some additional handling.
489536 // See #63448 for details.
490- if (
491- is_callable ( array ( $ this ->image , 'getImageProperty ' ) )
492- && '3 ' === $ this ->image ->getImageProperty ( 'png:IHDR.color-type-orig ' )
493- ) {
537+ if ( $ is_indexed_png ) {
494538
495539 // Check for an alpha channel.
496- if (
497- is_callable ( array ( $ this ->image , 'getImageAlphaChannel ' ) )
498- && $ this ->image ->getImageAlphaChannel ()
499- ) {
540+ if ( $ is_indexed_png_with_alpha_channel ) {
500541 $ this ->image ->setOption ( 'png:include-chunk ' , 'tRNS ' );
501542 } else {
502543 $ this ->image ->setOption ( 'png:exclude-chunk ' , 'all ' );
503544 }
504- // Set the image format to Indexed PNG.
505- $ this ->image ->setOption ( 'png:format ' , 'png8 ' );
506545
546+ $ this ->image ->quantizeImage ( 256 , $ this ->image ->getColorspace (), 0 , false , false );
547+
548+ /*
549+ * If the colorspace is 'gray', use the png8 format to ensure it stays indexed.
550+ * ImageMagick tends to save grayscale images as grayscale PNGs rather than indexed PNGs,
551+ * even though grayscale PNGs usually have considerably larger file sizes.
552+ * But we can force ImageMagick to save the image as an indexed PNG instead,
553+ * by telling it to use png8 format.
554+ *
555+ * Note that we need to first call quantizeImage() before checking getImageColorspace(),
556+ * because only after calling quantizeImage() will the colorspace be COLORSPACE_GRAY for grayscale images
557+ * (and we have not found any other way to identify grayscale images).
558+ *
559+ * We need to avoid forcing indexed format for images with true alpha transparency,
560+ * because ImageMagick does not support saving an image with true alpha transparency as an indexed PNG.
561+ */
562+ if ( Imagick::COLORSPACE_GRAY === $ this ->image ->getImageColorspace () && ! $ is_indexed_png_with_true_alpha_transparency ) {
563+ // Set the image format to Indexed PNG.
564+ $ this ->image ->setOption ( 'png:format ' , 'png8 ' );
565+ }
507566 } else {
508567 $ this ->image ->setOption ( 'png:exclude-chunk ' , 'all ' );
509568 }
0 commit comments