From 50b873d7449883d85c2bb31968abaf70cd9de1f8 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 16 Dec 2023 12:28:44 +0100 Subject: [PATCH] Add Image::reduceColors() --- .../Gd/Modifiers/LimitColorsModifier.php | 62 ------------------- .../Gd/Modifiers/QuantizeColorsModifier.php | 56 +++++++++++++++++ ...odifier.php => QuantizeColorsModifier.php} | 14 ++--- src/Image.php | 11 ++++ src/Interfaces/ImageInterface.php | 8 +++ src/Modifiers/LimitColorsModifier.php | 12 ---- src/Modifiers/QuantizeColorsModifier.php | 12 ++++ 7 files changed, 94 insertions(+), 81 deletions(-) delete mode 100644 src/Drivers/Gd/Modifiers/LimitColorsModifier.php create mode 100644 src/Drivers/Gd/Modifiers/QuantizeColorsModifier.php rename src/Drivers/Imagick/Modifiers/{LimitColorsModifier.php => QuantizeColorsModifier.php} (59%) delete mode 100644 src/Modifiers/LimitColorsModifier.php create mode 100644 src/Modifiers/QuantizeColorsModifier.php diff --git a/src/Drivers/Gd/Modifiers/LimitColorsModifier.php b/src/Drivers/Gd/Modifiers/LimitColorsModifier.php deleted file mode 100644 index d6357ffe..00000000 --- a/src/Drivers/Gd/Modifiers/LimitColorsModifier.php +++ /dev/null @@ -1,62 +0,0 @@ -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) { - // limitied color image must be palette (indexed) GDImage - $reduced = imagecreate($width, $height); - - // save alpha channel - imagesavealpha($reduced, true); - - // create matte - $matte = imagecolorallocatealpha($reduced, 255, 255, 255, 127); - - // fill with matte - imagefill($reduced, 0, 0, $matte); - - // disable alpha blending for the copy process - imagealphablending($reduced, false); - - // set transparency and get transparency index - imagecolortransparent($reduced, $matte); - - // copy original image (colors are limited automatically in the copy process) - imagecopy($reduced, $frame->native(), 0, 0, 0, 0, $width, $height); - - // reduce limit by one to include possible transparency in palette - // $limit = imagecolortransparent($frame->native()) === -1 ? $this->limit : $this->limit - 1; - - // decrease colors - // imagetruecolortopalette($reduced, true, $limit); - - $frame->setNative($reduced); - } - - return $image; - } -} diff --git a/src/Drivers/Gd/Modifiers/QuantizeColorsModifier.php b/src/Drivers/Gd/Modifiers/QuantizeColorsModifier.php new file mode 100644 index 00000000..ca344b3c --- /dev/null +++ b/src/Drivers/Gd/Modifiers/QuantizeColorsModifier.php @@ -0,0 +1,56 @@ +limit <= 0) { + throw new InputException('Quantization limit must be greater than 0.'); + } + + // no color reduction if the limit is higher than the colors in the img + $colorCount = imagecolorstotal($image->core()->native()); + if ($colorCount > 0 && $this->limit > $colorCount) { + return $image; + } + + $width = $image->width(); + $height = $image->height(); + + $background = $this->driver()->colorProcessor($image->colorspace())->colorToNative( + $this->driver()->handleInput($this->background) + ); + + foreach ($image as $frame) { + // create new image for color quantization + $reduced = imagecreatetruecolor($width, $height); + + // fill with background + imagefill($reduced, 0, 0, $background); + + // set transparency + imagecolortransparent($reduced, $background); + + // copy original image (colors are limited automatically in the copy process) + imagecopy($reduced, $frame->native(), 0, 0, 0, 0, $width, $height); + + // gd library does not support color quantization directly therefore the + // colors are decrease by transforming the image to a palette version + imagetruecolortopalette($reduced, true, $this->limit); + + $frame->setNative($reduced); + } + + return $image; + } +} diff --git a/src/Drivers/Imagick/Modifiers/LimitColorsModifier.php b/src/Drivers/Imagick/Modifiers/QuantizeColorsModifier.php similarity index 59% rename from src/Drivers/Imagick/Modifiers/LimitColorsModifier.php rename to src/Drivers/Imagick/Modifiers/QuantizeColorsModifier.php index 39129506..13e7963c 100644 --- a/src/Drivers/Imagick/Modifiers/LimitColorsModifier.php +++ b/src/Drivers/Imagick/Modifiers/QuantizeColorsModifier.php @@ -3,23 +3,23 @@ namespace Intervention\Image\Drivers\Imagick\Modifiers; use Intervention\Image\Drivers\DriverSpecializedModifier; +use Intervention\Image\Exceptions\InputException; use Intervention\Image\Interfaces\ImageInterface; /** * @property int $limit - * @property int $threshold + * @property mixed $background */ -class LimitColorsModifier extends DriverSpecializedModifier +class QuantizeColorsModifier extends DriverSpecializedModifier { public function apply(ImageInterface $image): ImageInterface { - // no color limit: no reduction - if ($this->limit === 0) { - return $image; + if ($this->limit <= 0) { + throw new InputException('Quantization limit must be greater than 0.'); } - // limit is over threshold: no reduction - if ($this->limit > $this->threshold) { + // no color reduction if the limit is higher than the colors in the img + if ($this->limit > $image->core()->native()->getImageColors()) { return $image; } diff --git a/src/Image.php b/src/Image.php index dd382d7a..4d44bd5a 100644 --- a/src/Image.php +++ b/src/Image.php @@ -64,6 +64,7 @@ use Intervention\Image\Modifiers\PixelateModifier; use Intervention\Image\Modifiers\PlaceModifier; use Intervention\Image\Modifiers\ProfileModifier; use Intervention\Image\Modifiers\ProfileRemovalModifier; +use Intervention\Image\Modifiers\QuantizeColorsModifier; use Intervention\Image\Modifiers\RemoveAnimationModifier; use Intervention\Image\Modifiers\ResizeCanvasModifier; use Intervention\Image\Modifiers\ResizeCanvasRelativeModifier; @@ -366,6 +367,16 @@ final class Image implements ImageInterface, Countable return $this->modify(new ProfileRemovalModifier()); } + /** + * {@inheritdoc} + * + * @see ImageInterface::reduceColors() + */ + public function reduceColors(int $limit, mixed $background = 'transparent'): ImageInterface + { + return $this->modify(new QuantizeColorsModifier($limit, $background)); + } + /** * {@inheritdoc} * diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 4bfa73af..8d9475cd 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -198,6 +198,14 @@ interface ImageInterface extends IteratorAggregate, Countable */ public function removeProfile(): ImageInterface; + /** + * Apply color quantization to the current image + * + * @param int $limit + * @return ImageInterface + */ + public function reduceColors(int $limit): ImageInterface; + /** * Sharpen the current image with given strength * diff --git a/src/Modifiers/LimitColorsModifier.php b/src/Modifiers/LimitColorsModifier.php deleted file mode 100644 index e892bd01..00000000 --- a/src/Modifiers/LimitColorsModifier.php +++ /dev/null @@ -1,12 +0,0 @@ -