From b5e31e574fb19479d210cdd0addf473713477db2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 31 Oct 2023 18:13:59 +0100 Subject: [PATCH] Refactor color reduction to modifer --- src/Drivers/Gd/Encoders/BmpEncoder.php | 10 ++-- src/Drivers/Gd/Encoders/GifEncoder.php | 10 ++-- src/Drivers/Gd/Encoders/PngEncoder.php | 10 ++-- .../Gd/Modifiers/LimitColorsModifier.php | 60 +++++++++++++++++++ src/Drivers/Gd/Traits/CanReduceColors.php | 59 ------------------ src/Drivers/Imagick/Encoders/BmpEncoder.php | 6 +- src/Drivers/Imagick/Encoders/GifEncoder.php | 9 ++- src/Drivers/Imagick/Encoders/PngEncoder.php | 6 +- .../Imagick/Modifiers/LimitColorsModifier.php | 44 ++++++++++++++ .../Imagick/Traits/CanReduceColors.php | 38 ------------ 10 files changed, 126 insertions(+), 126 deletions(-) create mode 100644 src/Drivers/Gd/Modifiers/LimitColorsModifier.php delete mode 100644 src/Drivers/Gd/Traits/CanReduceColors.php create mode 100644 src/Drivers/Imagick/Modifiers/LimitColorsModifier.php delete mode 100644 src/Drivers/Imagick/Traits/CanReduceColors.php diff --git a/src/Drivers/Gd/Encoders/BmpEncoder.php b/src/Drivers/Gd/Encoders/BmpEncoder.php index 326987a06..a3e95b40f 100644 --- a/src/Drivers/Gd/Encoders/BmpEncoder.php +++ b/src/Drivers/Gd/Encoders/BmpEncoder.php @@ -3,15 +3,13 @@ namespace Intervention\Image\Drivers\Gd\Encoders; use Intervention\Image\Drivers\Abstract\Encoders\AbstractEncoder; -use Intervention\Image\Drivers\Gd\Traits\CanReduceColors; +use Intervention\Image\Drivers\Gd\Modifiers\LimitColorsModifier; use Intervention\Image\EncodedImage; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; class BmpEncoder extends AbstractEncoder implements EncoderInterface { - use CanReduceColors; - public function __construct(protected int $color_limit = 0) { // @@ -19,9 +17,9 @@ public function __construct(protected int $color_limit = 0) public function encode(ImageInterface $image): EncodedImage { - $gd = $this->maybeReduceColors($image->frame()->core(), $this->color_limit); - $data = $this->getBuffered(function () use ($gd) { - imagebmp($gd, null, false); + $image = $image->modify(new LimitColorsModifier($this->color_limit)); + $data = $this->getBuffered(function () use ($image) { + imagebmp($image->frame()->core(), null, false); }); return new EncodedImage($data, 'image/bmp'); diff --git a/src/Drivers/Gd/Encoders/GifEncoder.php b/src/Drivers/Gd/Encoders/GifEncoder.php index d69797056..5967f5cd6 100644 --- a/src/Drivers/Gd/Encoders/GifEncoder.php +++ b/src/Drivers/Gd/Encoders/GifEncoder.php @@ -4,15 +4,13 @@ use Intervention\Gif\Builder as GifBuilder; use Intervention\Image\Drivers\Abstract\Encoders\AbstractEncoder; -use Intervention\Image\Drivers\Gd\Traits\CanReduceColors; +use Intervention\Image\Drivers\Gd\Modifiers\LimitColorsModifier; use Intervention\Image\EncodedImage; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; class GifEncoder extends AbstractEncoder implements EncoderInterface { - use CanReduceColors; - public function __construct(protected int $color_limit = 0) { // @@ -24,9 +22,9 @@ public function encode(ImageInterface $image): EncodedImage return $this->encodeAnimated($image); } - $gd = $this->maybeReduceColors($image->frame()->core(), $this->color_limit); - $data = $this->getBuffered(function () use ($gd) { - imagegif($gd); + $image = $image->modify(new LimitColorsModifier($this->color_limit)); + $data = $this->getBuffered(function () use ($image) { + imagegif($image->frame()->core()); }); return new EncodedImage($data, 'image/gif'); diff --git a/src/Drivers/Gd/Encoders/PngEncoder.php b/src/Drivers/Gd/Encoders/PngEncoder.php index e74069fdd..7daa2dc65 100644 --- a/src/Drivers/Gd/Encoders/PngEncoder.php +++ b/src/Drivers/Gd/Encoders/PngEncoder.php @@ -3,15 +3,13 @@ namespace Intervention\Image\Drivers\Gd\Encoders; use Intervention\Image\Drivers\Abstract\Encoders\AbstractEncoder; -use Intervention\Image\Drivers\Gd\Traits\CanReduceColors; +use Intervention\Image\Drivers\Gd\Modifiers\LimitColorsModifier; use Intervention\Image\EncodedImage; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; class PngEncoder extends AbstractEncoder implements EncoderInterface { - use CanReduceColors; - public function __construct(protected int $color_limit = 0) { // @@ -19,9 +17,9 @@ public function __construct(protected int $color_limit = 0) public function encode(ImageInterface $image): EncodedImage { - $gd = $this->maybeReduceColors($image->frame()->core(), $this->color_limit); - $data = $this->getBuffered(function () use ($gd) { - imagepng($gd, null, -1); + $image = $image->modify(new LimitColorsModifier($this->color_limit)); + $data = $this->getBuffered(function () use ($image) { + imagepng($image->frame()->core(), null, -1); }); return new EncodedImage($data, 'image/png'); diff --git a/src/Drivers/Gd/Modifiers/LimitColorsModifier.php b/src/Drivers/Gd/Modifiers/LimitColorsModifier.php new file mode 100644 index 000000000..84835ef40 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/LimitColorsModifier.php @@ -0,0 +1,60 @@ +limit === 0) { + return $image; + } + + // limit is over threshold: no reduction + if ($this->limit > $this->threshold) { + return $image; + } + + $width = $image->width(); + $height = $image->height(); + + foreach ($image as $frame) { + // create empty gd + $reduced = imagecreatetruecolor($width, $height); + + // create matte + $matte = imagecolorallocatealpha($reduced, 255, 255, 255, 127); + + // fill with matte + imagefill($reduced, 0, 0, $matte); + + imagealphablending($reduced, false); + + // set transparency and get transparency index + imagecolortransparent($reduced, $matte); + + // copy original image + imagecopy($reduced, $frame->core(), 0, 0, 0, 0, $width, $height); + + // reduce limit by one to include possible transparency in palette + $limit = imagecolortransparent($frame->core()) === -1 ? $this->limit : $this->limit - 1; + + // decrease colors + imagetruecolortopalette($reduced, true, $limit); + + $frame->setCore($reduced); + } + + + return $image; + } +} diff --git a/src/Drivers/Gd/Traits/CanReduceColors.php b/src/Drivers/Gd/Traits/CanReduceColors.php deleted file mode 100644 index 1c110bd9a..000000000 --- a/src/Drivers/Gd/Traits/CanReduceColors.php +++ /dev/null @@ -1,59 +0,0 @@ - $threshold) { - return $gd; - } - - // image size - $width = imagesx($gd); - $height = imagesy($gd); - - // create empty gd - $reduced = imagecreatetruecolor($width, $height); - - // create matte - $matte = imagecolorallocatealpha($reduced, 255, 255, 255, 127); - - // fill with matte - imagefill($reduced, 0, 0, $matte); - - imagealphablending($reduced, false); - - // set transparency and get transparency index - imagecolortransparent($reduced, $matte); - - // copy original image - imagecopy($reduced, $gd, 0, 0, 0, 0, $width, $height); - - // reduce limit by one to include possible transparency in palette - $limit = imagecolortransparent($gd) === -1 ? $limit : $limit - 1; - - // decrease colors - imagetruecolortopalette($reduced, true, $limit); - - return $reduced; - } -} diff --git a/src/Drivers/Imagick/Encoders/BmpEncoder.php b/src/Drivers/Imagick/Encoders/BmpEncoder.php index 79767f4d9..ad07bdc94 100644 --- a/src/Drivers/Imagick/Encoders/BmpEncoder.php +++ b/src/Drivers/Imagick/Encoders/BmpEncoder.php @@ -4,15 +4,13 @@ use Imagick; use Intervention\Image\Drivers\Abstract\Encoders\AbstractEncoder; -use Intervention\Image\Drivers\Imagick\Traits\CanReduceColors; +use Intervention\Image\Drivers\Imagick\Modifiers\LimitColorsModifier; use Intervention\Image\EncodedImage; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; class BmpEncoder extends AbstractEncoder implements EncoderInterface { - use CanReduceColors; - public function __construct(protected int $color_limit = 0) { // @@ -23,12 +21,12 @@ public function encode(ImageInterface $image): EncodedImage $format = 'bmp'; $compression = Imagick::COMPRESSION_NO; + $image = $image->modify(new LimitColorsModifier($this->color_limit)); $imagick = $image->frame()->core(); $imagick->setFormat($format); $imagick->setImageFormat($format); $imagick->setCompression($compression); $imagick->setImageCompression($compression); - $this->maybeReduceColors($imagick, $this->color_limit); return new EncodedImage($imagick->getImagesBlob(), 'image/bmp'); } diff --git a/src/Drivers/Imagick/Encoders/GifEncoder.php b/src/Drivers/Imagick/Encoders/GifEncoder.php index ef709b24c..2e56158b3 100644 --- a/src/Drivers/Imagick/Encoders/GifEncoder.php +++ b/src/Drivers/Imagick/Encoders/GifEncoder.php @@ -5,15 +5,16 @@ use Imagick; use Intervention\Image\Drivers\Abstract\Encoders\AbstractEncoder; use Intervention\Image\Drivers\Imagick\Image; -use Intervention\Image\Drivers\Imagick\Traits\CanReduceColors; +use Intervention\Image\Drivers\Imagick\Modifiers\LimitColorsModifier; use Intervention\Image\EncodedImage; use Intervention\Image\Exceptions\EncoderException; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Traits\CanCheckType; class GifEncoder extends AbstractEncoder implements EncoderInterface { - use CanReduceColors; + use CanCheckType; public function __construct(protected int $color_limit = 0) { @@ -29,13 +30,15 @@ public function encode(ImageInterface $image): EncodedImage throw new EncoderException('Image does not match the current driver.'); } + $image = $this->failIfNotClass($image, Image::class); + + $image = $image->modify(new LimitColorsModifier($this->color_limit)); $imagick = $image->getImagick(); $imagick->setFormat($format); $imagick->setImageFormat($format); $imagick->setCompression($compression); $imagick->setImageCompression($compression); $imagick->optimizeImageLayers(); - $this->maybeReduceColors($imagick, $this->color_limit); $imagick = $imagick->deconstructImages(); return new EncodedImage($imagick->getImagesBlob(), 'image/gif'); diff --git a/src/Drivers/Imagick/Encoders/PngEncoder.php b/src/Drivers/Imagick/Encoders/PngEncoder.php index aacf67632..4c31ca8b1 100644 --- a/src/Drivers/Imagick/Encoders/PngEncoder.php +++ b/src/Drivers/Imagick/Encoders/PngEncoder.php @@ -4,15 +4,13 @@ use Imagick; use Intervention\Image\Drivers\Abstract\Encoders\AbstractEncoder; -use Intervention\Image\Drivers\Imagick\Traits\CanReduceColors; +use Intervention\Image\Drivers\Imagick\Modifiers\LimitColorsModifier; use Intervention\Image\EncodedImage; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; class PngEncoder extends AbstractEncoder implements EncoderInterface { - use CanReduceColors; - public function __construct(protected int $color_limit = 0) { // @@ -23,12 +21,12 @@ public function encode(ImageInterface $image): EncodedImage $format = 'png'; $compression = Imagick::COMPRESSION_ZIP; + $image = $image->modify(new LimitColorsModifier($this->color_limit)); $imagick = $image->frame()->core(); $imagick->setFormat($format); $imagick->setImageFormat($format); $imagick->setCompression($compression); $imagick->setImageCompression($compression); - $this->maybeReduceColors($imagick, $this->color_limit); return new EncodedImage($imagick->getImagesBlob(), 'image/png'); } diff --git a/src/Drivers/Imagick/Modifiers/LimitColorsModifier.php b/src/Drivers/Imagick/Modifiers/LimitColorsModifier.php new file mode 100644 index 000000000..142794f58 --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/LimitColorsModifier.php @@ -0,0 +1,44 @@ +limit === 0) { + return $image; + } + + // limit is over threshold: no reduction + if ($this->limit > $this->threshold) { + return $image; + } + + $image = $this->failIfNotClass($image, Image::class); + foreach ($image->getImagick() as $core) { + $core->quantizeImage( + $this->limit, + $core->getImageColorspace(), + 0, + false, + false + ); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Traits/CanReduceColors.php b/src/Drivers/Imagick/Traits/CanReduceColors.php deleted file mode 100644 index 04b62d0bb..000000000 --- a/src/Drivers/Imagick/Traits/CanReduceColors.php +++ /dev/null @@ -1,38 +0,0 @@ - $threshold) { - return $imagick; - } - - $imagick->quantizeImage( - $limit, - $imagick->getImageColorspace(), - 0, - false, - false - ); - - return $imagick; - } -}