mirror of
https://github.com/Intervention/image.git
synced 2025-08-20 04:31:24 +02:00
Refactor color reduction to modifer
This commit is contained in:
@@ -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 @@ class BmpEncoder extends AbstractEncoder implements EncoderInterface
|
||||
|
||||
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');
|
||||
|
@@ -4,15 +4,13 @@ namespace Intervention\Image\Drivers\Gd\Encoders;
|
||||
|
||||
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 @@ class GifEncoder extends AbstractEncoder implements EncoderInterface
|
||||
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');
|
||||
|
@@ -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 @@ class PngEncoder extends AbstractEncoder implements EncoderInterface
|
||||
|
||||
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');
|
||||
|
60
src/Drivers/Gd/Modifiers/LimitColorsModifier.php
Normal file
60
src/Drivers/Gd/Modifiers/LimitColorsModifier.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Modifiers;
|
||||
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\ModifierInterface;
|
||||
|
||||
class LimitColorsModifier implements ModifierInterface
|
||||
{
|
||||
public function __construct(protected int $limit = 0, protected int $threshold = 256)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
// no color limit: no reduction
|
||||
if ($this->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;
|
||||
}
|
||||
}
|
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Traits;
|
||||
|
||||
use GdImage;
|
||||
|
||||
trait CanReduceColors
|
||||
{
|
||||
/**
|
||||
* Reduce colors in a given GdImage to the given limit. Reduction is only
|
||||
* applied when the given limit is under the given threshold
|
||||
*
|
||||
* @param GdImage $gd
|
||||
* @param int $limit
|
||||
* @param int $threshold
|
||||
* @return GdImage
|
||||
*/
|
||||
private function maybeReduceColors(GdImage $gd, int $limit, int $threshold = 256): GdImage
|
||||
{
|
||||
// no color limit: no reduction
|
||||
if ($limit === 0) {
|
||||
return $gd;
|
||||
}
|
||||
|
||||
// limit is over threshold: no reduction
|
||||
if ($limit > $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;
|
||||
}
|
||||
}
|
@@ -4,15 +4,13 @@ namespace Intervention\Image\Drivers\Imagick\Encoders;
|
||||
|
||||
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 @@ class BmpEncoder extends AbstractEncoder implements EncoderInterface
|
||||
$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');
|
||||
}
|
||||
|
@@ -5,15 +5,16 @@ namespace Intervention\Image\Drivers\Imagick\Encoders;
|
||||
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 @@ class GifEncoder extends AbstractEncoder implements EncoderInterface
|
||||
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');
|
||||
|
@@ -4,15 +4,13 @@ namespace Intervention\Image\Drivers\Imagick\Encoders;
|
||||
|
||||
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 @@ class PngEncoder extends AbstractEncoder implements EncoderInterface
|
||||
$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');
|
||||
}
|
||||
|
44
src/Drivers/Imagick/Modifiers/LimitColorsModifier.php
Normal file
44
src/Drivers/Imagick/Modifiers/LimitColorsModifier.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Intervention\Image\Drivers\Imagick\Modifiers;
|
||||
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\ModifierInterface;
|
||||
use Intervention\Image\Traits\CanCheckType;
|
||||
use Intervention\Image\Drivers\Imagick\Image;
|
||||
|
||||
class LimitColorsModifier implements ModifierInterface
|
||||
{
|
||||
use CanCheckType;
|
||||
|
||||
public function __construct(protected int $limit = 0, protected $threshold = 256)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function apply(ImageInterface $image): ImageInterface
|
||||
{
|
||||
// no color limit: no reduction
|
||||
if ($this->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;
|
||||
}
|
||||
}
|
@@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Intervention\Image\Drivers\Imagick\Traits;
|
||||
|
||||
use Imagick;
|
||||
|
||||
trait CanReduceColors
|
||||
{
|
||||
/**
|
||||
* Returns a Imagick from a given image with reduced colors to a given limit.
|
||||
* Reduction is only applied when the given limit is under the given threshold
|
||||
*
|
||||
* @param Imagick $imagick
|
||||
* @param int $limit
|
||||
* @param int $threshold
|
||||
* @return Imagick
|
||||
*/
|
||||
private function maybeReduceColors(Imagick $imagick, int $limit, int $threshold = 256): Imagick
|
||||
{
|
||||
if ($limit === 0) {
|
||||
return $imagick;
|
||||
}
|
||||
|
||||
if ($limit > $threshold) {
|
||||
return $imagick;
|
||||
}
|
||||
|
||||
$imagick->quantizeImage(
|
||||
$limit,
|
||||
$imagick->getImageColorspace(),
|
||||
0,
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
return $imagick;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user