1
0
mirror of https://github.com/Intervention/image.git synced 2025-01-17 12:18:14 +01:00

Implement 'indexed' option in PNG encoders

This commit is contained in:
Oliver Vogel 2024-08-03 10:56:34 +02:00
parent ce4d2bc75c
commit 66040006f3
No known key found for this signature in database
GPG Key ID: 1B19D214C02D69BB
3 changed files with 116 additions and 15 deletions

View File

@ -4,22 +4,72 @@ declare(strict_types=1);
namespace Intervention\Image\Drivers\Gd\Encoders;
use GdImage;
use Intervention\Image\Drivers\Gd\Cloner;
use Intervention\Image\EncodedImage;
use Intervention\Image\Encoders\PngEncoder as GenericPngEncoder;
use Intervention\Image\Exceptions\AnimationException;
use Intervention\Image\Exceptions\ColorException;
use Intervention\Image\Exceptions\RuntimeException;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\SpecializedInterface;
class PngEncoder extends GenericPngEncoder implements SpecializedInterface
{
/**
* {@inheritdoc}
*
* @see EncoderInterface::encode()
*/
public function encode(ImageInterface $image): EncodedImage
{
$gd = $image->core()->native();
$data = $this->buffered(function () use ($gd) {
imageinterlace($gd, $this->interlaced);
imagepng($gd, null, -1);
imageinterlace($gd, false);
$output = $this->prepareOutput($image);
// encode
$data = $this->buffered(function () use ($output) {
imageinterlace($output, $this->interlaced);
imagepng($output, null, -1);
});
return new EncodedImage($data, 'image/png');
}
/**
* Prepare given image instance for PNG format output according to encoder settings
*
* @param ImageInterface $image
* @param bool $indexed
* @throws RuntimeException
* @throws ColorException
* @throws AnimationException
* @return GdImage
*/
private function prepareOutput(ImageInterface $image): GdImage
{
if ($this->indexed === false) {
return Cloner::clone($image->core()->native());
}
// get blending color
$blendingColor = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->driver()->handleInput($this->driver()->config()->blendingColor)
);
// clone output instance
$output = Cloner::cloneEmpty($image->core()->native());
// fill with blending color
imagefill($output, 0, 0, $blendingColor);
// set transparency
imagecolortransparent($output, $blendingColor);
// copy original into output
imagecopy($output, $image->core()->native(), 0, 0, 0, 0, imagesx($output), imagesy($output));
// reduce to indexed color palette
imagetruecolortopalette($output, true, 255);
return $output;
}
}

View File

@ -5,28 +5,79 @@ declare(strict_types=1);
namespace Intervention\Image\Drivers\Imagick\Encoders;
use Imagick;
use ImagickException;
use Intervention\Image\EncodedImage;
use Intervention\Image\Encoders\PngEncoder as GenericPngEncoder;
use Intervention\Image\Exceptions\AnimationException;
use Intervention\Image\Exceptions\RuntimeException;
use Intervention\Image\Exceptions\ColorException;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\SpecializedInterface;
class PngEncoder extends GenericPngEncoder implements SpecializedInterface
{
/**
* {@inheritdoc}
*
* @see EncoderInterface::encode()
*/
public function encode(ImageInterface $image): EncodedImage
{
$format = 'PNG';
$compression = Imagick::COMPRESSION_ZIP;
$output = $this->prepareOutput($image);
$imagick = $image->core()->native();
$imagick->setFormat($format);
$imagick->setImageFormat($format);
$imagick->setCompression($compression);
$imagick->setImageCompression($compression);
$output->setCompression(Imagick::COMPRESSION_ZIP);
$output->setImageCompression(Imagick::COMPRESSION_ZIP);
if ($this->interlaced) {
$imagick->setInterlaceScheme(Imagick::INTERLACE_LINE);
$output->setInterlaceScheme(Imagick::INTERLACE_LINE);
}
return new EncodedImage($imagick->getImagesBlob(), 'image/png');
return new EncodedImage($output->getImagesBlob(), 'image/png');
}
/**
* Prepare given image instance for PNG format output according to encoder settings
*
* @param ImageInterface $image
* @throws AnimationException
* @throws RuntimeException
* @throws ColorException
* @throws ImagickException
* @return Imagick
*/
private function prepareOutput(ImageInterface $image): Imagick
{
if ($this->indexed === false) {
$output = clone $image->core()->native();
// ensure to encode PNG image type 6 true color alpha
$output->setFormat('PNG32');
$output->setImageFormat('PNG32');
return $output;
}
// get blending color
$blendingColor = $this->driver()->colorProcessor($image->colorspace())->colorToNative(
$this->driver()->handleInput($this->driver()->config()->blendingColor)
);
// create new image with blending color as background
$output = new Imagick();
$output->newImage($image->width(), $image->height(), $blendingColor, 'PNG');
// set transparency of original image
$output->compositeImage($image->core()->native(), Imagick::COMPOSITE_DSTIN, 0, 0);
$output->transparentPaintImage('#000000', 0, 0, false);
// copy original and create indexed color palette version
$output->compositeImage($image->core()->native(), Imagick::COMPOSITE_DEFAULT, 0, 0);
$output->quantizeImage(255, $output->getImageColorSpace(), 0, false, false);
// ensure to encode PNG image type 3 (indexed)
$output->setFormat('PNG8');
$output->setImageFormat('PNG8');
return $output;
}
}

View File

@ -8,7 +8,7 @@ use Intervention\Image\Drivers\SpecializableEncoder;
class PngEncoder extends SpecializableEncoder
{
public function __construct(public bool $interlaced = false)
public function __construct(public bool $interlaced = false, public bool $indexed = false)
{
}
}