Skip to content

Commit

Permalink
Refactor color reduction to modifer
Browse files Browse the repository at this point in the history
  • Loading branch information
olivervogel committed Oct 31, 2023
1 parent 92c37c5 commit b5e31e5
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 126 deletions.
10 changes: 4 additions & 6 deletions src/Drivers/Gd/Encoders/BmpEncoder.php
Expand Up @@ -3,25 +3,23 @@
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)
{
//
}

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');
Expand Down
10 changes: 4 additions & 6 deletions src/Drivers/Gd/Encoders/GifEncoder.php
Expand Up @@ -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)
{
//
Expand All @@ -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');
Expand Down
10 changes: 4 additions & 6 deletions src/Drivers/Gd/Encoders/PngEncoder.php
Expand Up @@ -3,25 +3,23 @@
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)
{
//
}

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');
Expand Down
60 changes: 60 additions & 0 deletions src/Drivers/Gd/Modifiers/LimitColorsModifier.php
@@ -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;
}
}
59 changes: 0 additions & 59 deletions src/Drivers/Gd/Traits/CanReduceColors.php

This file was deleted.

6 changes: 2 additions & 4 deletions src/Drivers/Imagick/Encoders/BmpEncoder.php
Expand Up @@ -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)
{
//
Expand All @@ -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');
}
Expand Down
9 changes: 6 additions & 3 deletions src/Drivers/Imagick/Encoders/GifEncoder.php
Expand Up @@ -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)
{
Expand All @@ -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');
Expand Down
6 changes: 2 additions & 4 deletions src/Drivers/Imagick/Encoders/PngEncoder.php
Expand Up @@ -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)
{
//
Expand All @@ -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');
}
Expand Down
44 changes: 44 additions & 0 deletions src/Drivers/Imagick/Modifiers/LimitColorsModifier.php
@@ -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;
}
}
38 changes: 0 additions & 38 deletions src/Drivers/Imagick/Traits/CanReduceColors.php

This file was deleted.

0 comments on commit b5e31e5

Please sign in to comment.