From b198fe6a3ae589785b0833532010e4f433aa4a94 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 6 Jan 2024 12:37:41 +0100 Subject: [PATCH] Implement Blending Color --- src/Drivers/Gd/Cloner.php | 89 +++++++++++++++++++ .../Gd/Decoders/BinaryImageDecoder.php | 11 ++- src/Drivers/Gd/Encoders/JpegEncoder.php | 8 +- src/Drivers/Gd/Frame.php | 31 +------ src/Drivers/Gd/Modifiers/ContainModifier.php | 24 ++--- src/Drivers/Gd/Modifiers/CoverModifier.php | 24 +---- src/Drivers/Gd/Modifiers/CropModifier.php | 24 +---- .../Gd/Modifiers/QuantizeColorsModifier.php | 6 +- .../Gd/Modifiers/ResizeCanvasModifier.php | 15 +--- src/Drivers/Gd/Modifiers/ResizeModifier.php | 29 +----- src/Drivers/Gd/Modifiers/RotateModifier.php | 11 +-- src/Drivers/Gd/SpecializedModifier.php | 8 -- src/Drivers/Imagick/Encoders/JpegEncoder.php | 10 ++- src/Drivers/Imagick/Frame.php | 4 + src/Image.php | 34 +++++++ src/Interfaces/ImageInterface.php | 17 ++++ tests/Drivers/Gd/ClonerTest.php | 69 ++++++++++++++ tests/Drivers/Gd/ImageTest.php | 15 +++- .../Gd/Modifiers/ContainModifierTest.php | 2 +- .../Gd/Modifiers/FlipFlopModifierTest.php | 4 +- tests/Drivers/Imagick/ImageTest.php | 11 +++ 21 files changed, 286 insertions(+), 160 deletions(-) create mode 100644 src/Drivers/Gd/Cloner.php create mode 100644 tests/Drivers/Gd/ClonerTest.php diff --git a/src/Drivers/Gd/Cloner.php b/src/Drivers/Gd/Cloner.php new file mode 100644 index 00000000..6b6fd0ad --- /dev/null +++ b/src/Drivers/Gd/Cloner.php @@ -0,0 +1,89 @@ + new Rectangle(imagesx($gd), imagesy($gd)), + default => $size, + }; + + // create new gd image with same size or new given size + $clone = imagecreatetruecolor($size->width(), $size->height()); + + // copy resolution to clone + $resolution = imageresolution($gd); + if (is_array($resolution) && array_key_exists(0, $resolution) && array_key_exists(1, $resolution)) { + imageresolution($clone, $resolution[0], $resolution[1]); + } + + // fill with background + $processor = new ColorProcessor(); + imagefill($clone, 0, 0, $processor->colorToNative($background)); + imagealphablending($clone, true); + imagesavealpha($clone, true); + + return $clone; + } + + /** + * Create a clone of an GdImage that is positioned on the specified background color. + * Possible transparent areas are mixed with this color. + * + * @param GdImage $gd + * @param ColorInterface $background + * @return GdImage + */ + public static function cloneBlended(GdImage $gd, ColorInterface $background): GdImage + { + // create empty canvas with same size + $clone = static::cloneEmpty($gd, background: $background); + + // transfer actual image to clone + imagecopy($clone, $gd, 0, 0, 0, 0, imagesx($gd), imagesy($gd)); + + return $clone; + } +} diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index 797b8b19..837e837e 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -9,6 +9,7 @@ use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Gif\Decoder as GifDecoder; use Intervention\Gif\Splitter as GifSplitter; +use Intervention\Image\Drivers\Gd\Cloner; use Intervention\Image\Drivers\Gd\Core; use Intervention\Image\Drivers\Gd\Driver; use Intervention\Image\Exceptions\DecoderException; @@ -38,17 +39,15 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface throw new DecoderException('Unable to decode input'); } - if (!imageistruecolor($gd)) { - imagepalettetotruecolor($gd); - } - - imagesavealpha($gd, true); + // clone image to normalize transparency to #ffffff00 + $normalized = Cloner::clone($gd); + imagedestroy($gd); // build image instance $image = new Image( new Driver(), new Core([ - new Frame($gd) + new Frame($normalized) ]), $this->extractExifData($input) ); diff --git a/src/Drivers/Gd/Encoders/JpegEncoder.php b/src/Drivers/Gd/Encoders/JpegEncoder.php index 3493f67e..e9a456cb 100644 --- a/src/Drivers/Gd/Encoders/JpegEncoder.php +++ b/src/Drivers/Gd/Encoders/JpegEncoder.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Drivers\Gd\Encoders; use Intervention\Image\Drivers\DriverSpecializedEncoder; +use Intervention\Image\Drivers\Gd\Cloner; use Intervention\Image\EncodedImage; use Intervention\Image\Interfaces\ImageInterface; @@ -13,9 +14,10 @@ class JpegEncoder extends DriverSpecializedEncoder { public function encode(ImageInterface $image): EncodedImage { - $gd = $image->core()->native(); - $data = $this->getBuffered(function () use ($gd) { - imagejpeg($gd, null, $this->quality); + $output = Cloner::cloneBlended($image->core()->native(), background: $image->blendingColor()); + + $data = $this->getBuffered(function () use ($output) { + imagejpeg($output, null, $this->quality); }); return new EncodedImage($data, 'image/jpeg'); diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index cb0a2e33..62f4ea17 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -107,35 +107,6 @@ class Frame implements FrameInterface */ public function __clone(): void { - // create new clone image - $width = imagesx($this->native); - $height = imagesy($this->native); - $clone = match (imageistruecolor($this->native)) { - true => imagecreatetruecolor($width, $height), - default => imagecreate($width, $height), - }; - - // transfer resolution to clone - $resolution = imageresolution($this->native); - if (is_array($resolution) && array_key_exists(0, $resolution) && array_key_exists(1, $resolution)) { - imageresolution($clone, $resolution[0], $resolution[1]); - } - - // transfer transparency to clone - $transIndex = imagecolortransparent($this->native); - if ($transIndex != -1) { - $rgba = imagecolorsforindex($clone, $transIndex); - $transColor = imagecolorallocatealpha($clone, $rgba['red'], $rgba['green'], $rgba['blue'], 127); - imagefill($clone, 0, 0, $transColor); - imagecolortransparent($clone, $transColor); - } else { - imagealphablending($clone, false); - imagesavealpha($clone, true); - } - - // transfer actual image to clone - imagecopy($clone, $this->native, 0, 0, 0, 0, $width, $height); - - $this->native = $clone; + $this->native = Cloner::clone($this->native); } } diff --git a/src/Drivers/Gd/Modifiers/ContainModifier.php b/src/Drivers/Gd/Modifiers/ContainModifier.php index 5d7e3b3e..ba9f0af3 100644 --- a/src/Drivers/Gd/Modifiers/ContainModifier.php +++ b/src/Drivers/Gd/Modifiers/ContainModifier.php @@ -5,12 +5,12 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Colors\Rgb\Channels\Blue; use Intervention\Image\Colors\Rgb\Channels\Green; use Intervention\Image\Colors\Rgb\Channels\Red; +use Intervention\Image\Drivers\Gd\Cloner; use Intervention\Image\Drivers\Gd\SpecializedModifier; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; -use Intervention\Image\Modifiers\FillModifier; /** * @method SizeInterface getCropSize(ImageInterface $image) @@ -27,9 +27,10 @@ class ContainModifier extends SpecializedModifier $crop = $this->getCropSize($image); $resize = $this->getResizeSize($image); $background = $this->driver()->handleInput($this->background); + $blendingColor = $image->blendingColor(); foreach ($image as $frame) { - $this->modify($frame, $crop, $resize, $background); + $this->modify($frame, $crop, $resize, $background, $blendingColor); } return $image; @@ -39,26 +40,19 @@ class ContainModifier extends SpecializedModifier FrameInterface $frame, SizeInterface $crop, SizeInterface $resize, - ColorInterface $background + ColorInterface $background, + ColorInterface $blendingColor ): void { // create new gd image - $modified = $this->driver()->createImage( - $resize->width(), - $resize->height() - )->modify( - new FillModifier($background) - )->core()->native(); - - // retain resolution - $this->copyResolution($frame->native(), $modified); + $modified = Cloner::cloneEmpty($frame->native(), $resize, $background); // make image area transparent to keep transparency // even if background-color is set $transparent = imagecolorallocatealpha( $modified, - $background->channel(Red::class)->value(), - $background->channel(Green::class)->value(), - $background->channel(Blue::class)->value(), + $blendingColor->channel(Red::class)->value(), + $blendingColor->channel(Green::class)->value(), + $blendingColor->channel(Blue::class)->value(), 127, ); imagealphablending($modified, false); // do not blend / just overwrite diff --git a/src/Drivers/Gd/Modifiers/CoverModifier.php b/src/Drivers/Gd/Modifiers/CoverModifier.php index ce2ef59d..45026bcc 100644 --- a/src/Drivers/Gd/Modifiers/CoverModifier.php +++ b/src/Drivers/Gd/Modifiers/CoverModifier.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Gd\Cloner; use Intervention\Image\Drivers\Gd\SpecializedModifier; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -28,31 +29,12 @@ class CoverModifier extends SpecializedModifier protected function modifyFrame(FrameInterface $frame, SizeInterface $crop, SizeInterface $resize): void { // create new image - $modified = $this->driver()->createImage( - $resize->width(), - $resize->height() - )->core()->native(); - - // get original image - $original = $frame->native(); - - // retain resolution - $this->copyResolution($original, $modified); - - // preserve transparency - $transIndex = imagecolortransparent($original); - - if ($transIndex != -1) { - $rgba = imagecolorsforindex($modified, $transIndex); - $transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127); - imagefill($modified, 0, 0, $transColor); - imagecolortransparent($modified, $transColor); - } + $modified = Cloner::cloneEmpty($frame->native(), $resize); // copy content from resource imagecopyresampled( $modified, - $original, + $frame->native(), 0, 0, $crop->pivot()->x(), diff --git a/src/Drivers/Gd/Modifiers/CropModifier.php b/src/Drivers/Gd/Modifiers/CropModifier.php index 4d4b8c65..60d9c323 100644 --- a/src/Drivers/Gd/Modifiers/CropModifier.php +++ b/src/Drivers/Gd/Modifiers/CropModifier.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Gd\Cloner; use Intervention\Image\Drivers\Gd\SpecializedModifier; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -28,31 +29,12 @@ class CropModifier extends SpecializedModifier protected function cropFrame(FrameInterface $frame, SizeInterface $resizeTo): void { // create new image - $modified = $this->driver() - ->createImage($resizeTo->width(), $resizeTo->height()) - ->core() - ->native(); - - // get original image - $original = $frame->native(); - - // retain resolution - $this->copyResolution($original, $modified); - - // preserve transparency - $transIndex = imagecolortransparent($original); - - if ($transIndex != -1) { - $rgba = imagecolorsforindex($modified, $transIndex); - $transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127); - imagefill($modified, 0, 0, $transColor); - imagecolortransparent($modified, $transColor); - } + $modified = Cloner::cloneEmpty($frame->native(), $resizeTo); // copy content from resource imagecopyresampled( $modified, - $original, + $frame->native(), 0, 0, $resizeTo->pivot()->x() + $this->offset_x, diff --git a/src/Drivers/Gd/Modifiers/QuantizeColorsModifier.php b/src/Drivers/Gd/Modifiers/QuantizeColorsModifier.php index 8dbf1bd0..f1d5a165 100644 --- a/src/Drivers/Gd/Modifiers/QuantizeColorsModifier.php +++ b/src/Drivers/Gd/Modifiers/QuantizeColorsModifier.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Gd\Cloner; use Intervention\Image\Drivers\Gd\SpecializedModifier; use Intervention\Image\Exceptions\InputException; use Intervention\Image\Interfaces\ImageInterface; @@ -33,10 +34,7 @@ class QuantizeColorsModifier extends SpecializedModifier foreach ($image as $frame) { // create new image for color quantization - $reduced = imagecreatetruecolor($width, $height); - - // retain resolution - $this->copyResolution($frame->native(), $reduced); + $reduced = Cloner::cloneEmpty($frame->native(), background: $image->blendingColor()); // fill with background imagefill($reduced, 0, 0, $background); diff --git a/src/Drivers/Gd/Modifiers/ResizeCanvasModifier.php b/src/Drivers/Gd/Modifiers/ResizeCanvasModifier.php index 5035f339..0f273b4d 100644 --- a/src/Drivers/Gd/Modifiers/ResizeCanvasModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeCanvasModifier.php @@ -5,6 +5,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Colors\Rgb\Channels\Blue; use Intervention\Image\Colors\Rgb\Channels\Green; use Intervention\Image\Colors\Rgb\Channels\Red; +use Intervention\Image\Drivers\Gd\Cloner; use Intervention\Image\Drivers\Gd\SpecializedModifier; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\FrameInterface; @@ -35,16 +36,8 @@ class ResizeCanvasModifier extends SpecializedModifier SizeInterface $resize, ColorInterface $background, ): void { - // create new gd image - $modified = $this->driver()->createImage( - $resize->width(), - $resize->height() - )->modify( - new FillModifier($background) - )->core()->native(); - - // retain resolution - $this->copyResolution($frame->native(), $modified); + // create new canvas with target size & target background color + $modified = Cloner::cloneEmpty($frame->native(), $resize, $background); // make image area transparent to keep transparency // even if background-color is set @@ -57,7 +50,7 @@ class ResizeCanvasModifier extends SpecializedModifier ); imagealphablending($modified, false); // do not blend / just overwrite - imagecolortransparent($modified, $transparent); + // imagecolortransparent($modified, $transparent); imagefilledrectangle( $modified, $resize->pivot()->x() * -1, diff --git a/src/Drivers/Gd/Modifiers/ResizeModifier.php b/src/Drivers/Gd/Modifiers/ResizeModifier.php index 20d76494..5d19c49c 100644 --- a/src/Drivers/Gd/Modifiers/ResizeModifier.php +++ b/src/Drivers/Gd/Modifiers/ResizeModifier.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Drivers\Gd\Cloner; use Intervention\Image\Drivers\Gd\SpecializedModifier; use Intervention\Image\Interfaces\FrameInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -25,35 +26,13 @@ class ResizeModifier extends SpecializedModifier private function resizeFrame(FrameInterface $frame, SizeInterface $resizeTo): void { - // create new image - $modified = imagecreatetruecolor( - $resizeTo->width(), - $resizeTo->height() - ); - - // get current GDImage - $current = $frame->native(); - - // retain resolution - $this->copyResolution($current, $modified); - - // preserve transparency - $transIndex = imagecolortransparent($current); - - if ($transIndex != -1) { - $rgba = imagecolorsforindex($modified, $transIndex); - $transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127); - imagefill($modified, 0, 0, $transColor); - imagecolortransparent($modified, $transColor); - } else { - imagealphablending($modified, false); - imagesavealpha($modified, true); - } + // create empty canvas in target size + $modified = Cloner::cloneEmpty($frame->native(), $resizeTo); // copy content from resource imagecopyresampled( $modified, - $current, + $frame->native(), $resizeTo->pivot()->x(), $resizeTo->pivot()->y(), 0, diff --git a/src/Drivers/Gd/Modifiers/RotateModifier.php b/src/Drivers/Gd/Modifiers/RotateModifier.php index d59b74e8..955c0043 100644 --- a/src/Drivers/Gd/Modifiers/RotateModifier.php +++ b/src/Drivers/Gd/Modifiers/RotateModifier.php @@ -5,6 +5,7 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Colors\Rgb\Channels\Blue; use Intervention\Image\Colors\Rgb\Channels\Green; use Intervention\Image\Colors\Rgb\Channels\Red; +use Intervention\Image\Drivers\Gd\Cloner; use Intervention\Image\Drivers\Gd\SpecializedModifier; use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\ColorInterface; @@ -74,15 +75,7 @@ class RotateModifier extends SpecializedModifier ->rotate($this->rotationAngle() * -1); // create new gd image - $modified = $this->driver()->createImage( - imagesx($rotated), - imagesy($rotated) - )->modify(new FillModifier($background)) - ->core() - ->native(); - - // retain resolution - $this->copyResolution($frame->native(), $modified); + $modified = Cloner::cloneEmpty($frame->native(), $container, $background); // draw the cutout on new gd image to have a transparent // background where the rotated image will be placed diff --git a/src/Drivers/Gd/SpecializedModifier.php b/src/Drivers/Gd/SpecializedModifier.php index d006ace5..3a8c5f1a 100644 --- a/src/Drivers/Gd/SpecializedModifier.php +++ b/src/Drivers/Gd/SpecializedModifier.php @@ -2,16 +2,8 @@ namespace Intervention\Image\Drivers\Gd; -use GdImage; use Intervention\Image\Drivers\DriverSpecializedModifier; abstract class SpecializedModifier extends DriverSpecializedModifier { - protected function copyResolution(GdImage $source, GdImage $target): void - { - $resolution = imageresolution($source); - if (is_array($resolution) && array_key_exists(0, $resolution) && array_key_exists(1, $resolution)) { - imageresolution($target, $resolution[0], $resolution[1]); - } - } } diff --git a/src/Drivers/Imagick/Encoders/JpegEncoder.php b/src/Drivers/Imagick/Encoders/JpegEncoder.php index cf9719bb..c95ab1fa 100644 --- a/src/Drivers/Imagick/Encoders/JpegEncoder.php +++ b/src/Drivers/Imagick/Encoders/JpegEncoder.php @@ -17,15 +17,21 @@ class JpegEncoder extends DriverSpecializedEncoder $format = 'jpeg'; $compression = Imagick::COMPRESSION_JPEG; + // resolve blending color because jpeg has no transparency + $background = $this->driver() + ->colorProcessor($image->colorspace()) + ->colorToNative($image->blendingColor()); + $imagick = $image->core()->native(); - $imagick->setImageBackgroundColor('white'); - $imagick->setBackgroundColor('white'); + $imagick->setImageBackgroundColor($background); + $imagick->setBackgroundColor($background); $imagick->setFormat($format); $imagick->setImageFormat($format); $imagick->setCompression($compression); $imagick->setImageCompression($compression); $imagick->setCompressionQuality($this->quality); $imagick->setImageCompressionQuality($this->quality); + $imagick->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE); return new EncodedImage($imagick->getImagesBlob(), 'image/jpeg'); } diff --git a/src/Drivers/Imagick/Frame.php b/src/Drivers/Imagick/Frame.php index f957b962..342169b9 100644 --- a/src/Drivers/Imagick/Frame.php +++ b/src/Drivers/Imagick/Frame.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Drivers\Imagick; use Imagick; +use ImagickPixel; use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Image; use Intervention\Image\Interfaces\DriverInterface; @@ -14,6 +15,9 @@ class Frame implements FrameInterface { public function __construct(protected Imagick $native) { + $background = new ImagickPixel('rgba(255, 255, 255, 0)'); + $this->native->setImageBackgroundColor($background); + $this->native->setBackgroundColor($background); } /** diff --git a/src/Image.php b/src/Image.php index 72ea50bd..d44491aa 100644 --- a/src/Image.php +++ b/src/Image.php @@ -11,6 +11,7 @@ use Intervention\Image\Analyzers\PixelColorsAnalyzer; use Intervention\Image\Analyzers\ProfileAnalyzer; use Intervention\Image\Analyzers\ResolutionAnalyzer; use Intervention\Image\Analyzers\WidthAnalyzer; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Encoders\AutoEncoder; use Intervention\Image\Encoders\AvifEncoder; use Intervention\Image\Encoders\BmpEncoder; @@ -92,6 +93,14 @@ final class Image implements ImageInterface */ protected Origin $origin; + /** + * Color is mixed with transparent areas when converting to a format which + * does not support transparency. + * + * @var ColorInterface + */ + protected ColorInterface $blendingColor; + /** * Create new instance * @@ -106,6 +115,9 @@ final class Image implements ImageInterface protected CollectionInterface $exif = new Collection() ) { $this->origin = new Origin(); + $this->blendingColor = $this->colorspace()->importColor( + new Color(255, 255, 255, 0) + ); } /** @@ -368,6 +380,28 @@ final class Image implements ImageInterface return $this->analyze(new PixelColorsAnalyzer($x, $y)); } + /** + * {@inheritdoc} + * + * @see ImageInterface::blendingColor() + */ + public function blendingColor(): ColorInterface + { + return $this->blendingColor; + } + + /** + * {@inheritdoc} + * + * @see ImageInterface::setBlendingColor() + */ + public function setBlendingColor(mixed $color): ImageInterface + { + $this->blendingColor = $this->driver()->handleInput($color); + + return $this; + } + /** * {@inheritdoc} * diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index c50144d1..84dc34d1 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -185,6 +185,23 @@ interface ImageInterface extends IteratorAggregate, Countable */ public function pickColors(int $x, int $y): CollectionInterface; + /** + * Return color that is mixed with transparent areas when converting to a format which + * does not support transparency. + * + * @return ColorInterface + */ + public function blendingColor(): ColorInterface; + + /** + * Set blending color will have no effect unless image is converted into a format + * which does not support transparency. + * + * @param mixed $color + * @return ImageInterface + */ + public function setBlendingColor(mixed $color): ImageInterface; + /** * Retrieve ICC color profile of image * diff --git a/tests/Drivers/Gd/ClonerTest.php b/tests/Drivers/Gd/ClonerTest.php new file mode 100644 index 00000000..7c25a6e6 --- /dev/null +++ b/tests/Drivers/Gd/ClonerTest.php @@ -0,0 +1,69 @@ +getTestImagePath('gradient.gif')); + $clone = Cloner::clone($gd); + + $this->assertEquals(16, imagesx($gd)); + $this->assertEquals(16, imagesy($gd)); + $this->assertEquals(16, imagesx($clone)); + $this->assertEquals(16, imagesy($clone)); + + $this->assertEquals( + imagecolorsforindex($gd, imagecolorat($gd, 10, 10)), + imagecolorsforindex($clone, imagecolorat($clone, 10, 10)) + ); + } + + public function testCloneEmpty(): void + { + $gd = imagecreatefromgif($this->getTestImagePath('gradient.gif')); + $clone = Cloner::cloneEmpty($gd, new Rectangle(12, 12), new Color(255, 0, 0, 0)); + + $this->assertEquals(16, imagesx($gd)); + $this->assertEquals(16, imagesy($gd)); + $this->assertEquals(12, imagesx($clone)); + $this->assertEquals(12, imagesy($clone)); + + $this->assertEquals( + ['red' => 0, 'green' => 255, 'blue' => 2, 'alpha' => 0], + imagecolorsforindex($gd, imagecolorat($gd, 10, 10)), + ); + + $this->assertEquals( + ['red' => 255, 'green' => 0, 'blue' => 0, 'alpha' => 127], + imagecolorsforindex($clone, imagecolorat($clone, 10, 10)) + ); + } + + public function testCLoneBlended(): void + { + $gd = imagecreatefromgif($this->getTestImagePath('gradient.gif')); + $clone = Cloner::cloneBlended($gd, new Color(255, 0, 255, 255)); + + $this->assertEquals(16, imagesx($gd)); + $this->assertEquals(16, imagesy($gd)); + $this->assertEquals(16, imagesx($clone)); + $this->assertEquals(16, imagesy($clone)); + + $this->assertEquals( + ['red' => 0, 'green' => 0, 'blue' => 0, 'alpha' => 127], + imagecolorsforindex($gd, imagecolorat($gd, 1, 0)), + ); + + $this->assertEquals( + ['red' => 255, 'green' => 0, 'blue' => 255, 'alpha' => 0], + imagecolorsforindex($clone, imagecolorat($clone, 1, 0)) + ); + } +} diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 3f319472..a3c1385f 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -4,6 +4,7 @@ namespace Intervention\Image\Tests\Drivers\Gd; use Intervention\Image\Analyzers\WidthAnalyzer; use Intervention\Image\Collection; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Gd\Core; use Intervention\Image\Drivers\Gd\Driver; use Intervention\Image\Drivers\Gd\Frame; @@ -53,10 +54,10 @@ class ImageTest extends TestCase $this->assertEquals(4, $result->width()); $this->assertEquals('ff0000', $image->pickColor(0, 0)->toHex()); - $this->assertEquals('00000000', $image->pickColor(1, 0)->toHex()); + $this->assertTransparency($image->pickColor(1, 0)); $this->assertEquals('ff0000', $clone->pickColor(0, 0)->toHex()); - $this->assertEquals('00000000', $clone->pickColor(1, 0)->toHex()); + $this->assertTransparency($image->pickColor(1, 0)); } public function testDriver(): void @@ -210,4 +211,14 @@ class ImageTest extends TestCase { $this->assertInstanceOf(Image::class, $this->image->text('test', 0, 0, new Font())); } + + public function testSetGetBlendingColor(): void + { + $image = $this->readTestImage('gradient.gif'); + $this->assertInstanceOf(ColorInterface::class, $image->blendingColor()); + $this->assertColor(255, 255, 255, 0, $image->blendingColor()); + $result = $image->setBlendingColor(new Color(1, 2, 3, 4)); + $this->assertColor(1, 2, 3, 4, $result->blendingColor()); + $this->assertColor(1, 2, 3, 4, $image->blendingColor()); + } } diff --git a/tests/Drivers/Gd/Modifiers/ContainModifierTest.php b/tests/Drivers/Gd/Modifiers/ContainModifierTest.php index e648b5ed..4b999296 100644 --- a/tests/Drivers/Gd/Modifiers/ContainModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/ContainModifierTest.php @@ -23,7 +23,7 @@ class ContainModifierTest extends TestCase $this->assertEquals(200, $image->width()); $this->assertEquals(100, $image->height()); $this->assertColor(255, 255, 0, 255, $image->pickColor(0, 0)); - $this->assertColor(255, 255, 0, 0, $image->pickColor(140, 10)); // transparent + $this->assertTransparency($image->pickColor(140, 10)); $this->assertColor(255, 255, 0, 255, $image->pickColor(175, 10)); } } diff --git a/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php index 93279eb5..8d0fb5cf 100644 --- a/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php +++ b/tests/Drivers/Gd/Modifiers/FlipFlopModifierTest.php @@ -21,7 +21,7 @@ class FlipFlopModifierTest extends TestCase $image = $this->readTestImage('tile.png'); $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlipModifier()); - $this->assertEquals('00000000', $image->pickColor(0, 0)->toHex()); + $this->assertTransparency($image->pickColor(0, 0)); } public function testFlopImage(): void @@ -29,6 +29,6 @@ class FlipFlopModifierTest extends TestCase $image = $this->readTestImage('tile.png'); $this->assertEquals('b4e000', $image->pickColor(0, 0)->toHex()); $image->modify(new FlopModifier()); - $this->assertEquals('00000000', $image->pickColor(0, 0)->toHex()); + $this->assertTransparency($image->pickColor(0, 0)); } } diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php index 69257b07..001bd9c5 100644 --- a/tests/Drivers/Imagick/ImageTest.php +++ b/tests/Drivers/Imagick/ImageTest.php @@ -5,6 +5,7 @@ namespace Intervention\Image\Tests\Drivers\Imagick; use Imagick; use Intervention\Image\Analyzers\WidthAnalyzer; use Intervention\Image\Collection; +use Intervention\Image\Colors\Rgb\Color; use Intervention\Image\Drivers\Imagick\Core; use Intervention\Image\Drivers\Imagick\Driver; use Intervention\Image\Drivers\Imagick\Frame; @@ -204,4 +205,14 @@ class ImageTest extends TestCase { $this->assertInstanceOf(Image::class, $this->image->sharpen(12)); } + + public function testSetGetBlendingColor(): void + { + $image = $this->readTestImage('gradient.gif'); + $this->assertInstanceOf(ColorInterface::class, $image->blendingColor()); + $this->assertColor(255, 255, 255, 0, $image->blendingColor()); + $result = $image->setBlendingColor(new Color(1, 2, 3, 4)); + $this->assertColor(1, 2, 3, 4, $result->blendingColor()); + $this->assertColor(1, 2, 3, 4, $image->blendingColor()); + } }