From 61fae49be3b37b557860b42e73bd981e280af843 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 9 Dec 2023 16:44:22 +0100 Subject: [PATCH 1/2] Add tests for image cloning --- tests/ImageTest.php | 53 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tests/ImageTest.php diff --git a/tests/ImageTest.php b/tests/ImageTest.php new file mode 100644 index 00000000..c079ec6b --- /dev/null +++ b/tests/ImageTest.php @@ -0,0 +1,53 @@ +assertEquals(3, $image->width()); + $this->assertEquals(3, $clone->width()); + $result = $clone->resize(1); + $this->assertEquals(3, $image->width()); + $this->assertEquals(1, $clone->width()); + $this->assertEquals(1, $result->width()); + } + + public function testCloneImageImagick(): void + { + $imagick = new Imagick(); + $imagick->newImage(3, 2, new ImagickPixel('red'), 'png'); + $image = new Image( + new ImagickDriver(), + new ImagickCore($imagick) + ); + + $clone = clone $image; + + $this->assertEquals(3, $image->width()); + $this->assertEquals(3, $clone->width()); + $result = $clone->resize(1); + $this->assertEquals(3, $image->width()); + $this->assertEquals(1, $clone->width()); + $this->assertEquals(1, $result->width()); + } +} From 50e3e3308e7f7a301f11c1715cc6c609d11e5ce3 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 13 Dec 2023 17:28:07 +0100 Subject: [PATCH 2/2] Add image cloning --- src/Drivers/Gd/Core.php | 7 ++++ src/Drivers/Gd/Driver.php | 1 + src/Drivers/Gd/Frame.php | 39 +++++++++++++++++++++ src/Drivers/Imagick/Core.php | 5 +++ src/Image.php | 12 +++++++ tests/Drivers/Gd/ImageTest.php | 26 ++++++++++++++ tests/Drivers/Imagick/ImageTest.php | 27 +++++++++++++++ tests/ImageTest.php | 53 ----------------------------- 8 files changed, 117 insertions(+), 53 deletions(-) delete mode 100644 tests/ImageTest.php diff --git a/src/Drivers/Gd/Core.php b/src/Drivers/Gd/Core.php index 71c024e9..a9aab0fa 100644 --- a/src/Drivers/Gd/Core.php +++ b/src/Drivers/Gd/Core.php @@ -55,4 +55,11 @@ class Core extends Collection implements CoreInterface { return parent::last(); } + + public function __clone(): void + { + foreach ($this->items as $key => $frame) { + $this->items[$key] = clone $frame; + } + } } diff --git a/src/Drivers/Gd/Driver.php b/src/Drivers/Gd/Driver.php index 7c38b8f3..07d0d035 100644 --- a/src/Drivers/Gd/Driver.php +++ b/src/Drivers/Gd/Driver.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd; +use GdImage; use Intervention\Image\Drivers\AbstractDriver; use Intervention\Image\Image; use Intervention\Image\Interfaces\ColorInterface; diff --git a/src/Drivers/Gd/Frame.php b/src/Drivers/Gd/Frame.php index 5ac6c460..cb0a2e33 100644 --- a/src/Drivers/Gd/Frame.php +++ b/src/Drivers/Gd/Frame.php @@ -99,4 +99,43 @@ class Frame implements FrameInterface return $this; } + + /** + * This workaround helps cloning GdImages which is currently not possible. + * + * @return void + */ + 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; + } } diff --git a/src/Drivers/Imagick/Core.php b/src/Drivers/Imagick/Core.php index 6fde6d7b..ce0f041b 100644 --- a/src/Drivers/Imagick/Core.php +++ b/src/Drivers/Imagick/Core.php @@ -120,4 +120,9 @@ class Core implements CoreInterface, Iterator { return $this->frame($this->count()); } + + public function __clone(): void + { + $this->imagick = clone $this->imagick; + } } diff --git a/src/Image.php b/src/Image.php index 6601b21a..2815290e 100644 --- a/src/Image.php +++ b/src/Image.php @@ -774,4 +774,16 @@ final class Image implements ImageInterface, Countable { return $this->encode(new AvifEncoder($quality)); } + + /** + * Clone image + * + * @return void + */ + public function __clone(): void + { + $this->driver = clone $this->driver; + $this->core = clone $this->core; + $this->exif = clone $this->exif; + } } diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 8d8cfb9c..94c87b0d 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -4,6 +4,10 @@ namespace Intervention\Image\Tests\Drivers\Gd; use Intervention\Image\Analyzers\WidthAnalyzer; use Intervention\Image\Collection; +use Intervention\Image\Colors\Rgb\Channels\Alpha; +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\Core; use Intervention\Image\Drivers\Gd\Driver; use Intervention\Image\Drivers\Gd\Frame; @@ -17,10 +21,13 @@ use Intervention\Image\Interfaces\ResolutionInterface; use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Modifiers\GreyscaleModifier; use Intervention\Image\Tests\TestCase; +use Intervention\Image\Tests\Traits\CanCreateGdTestImage; use Intervention\Image\Typography\Font; class ImageTest extends TestCase { + use CanCreateGdTestImage; + protected Image $image; public function setUp(): void @@ -37,6 +44,25 @@ class ImageTest extends TestCase ); } + public function testClone(): void + { + $image = $this->readTestImage('gradient.gif'); + $clone = clone $image; + + $this->assertEquals(16, $image->width()); + $this->assertEquals(16, $clone->width()); + $result = $clone->crop(4, 4); + $this->assertEquals(16, $image->width()); + $this->assertEquals(4, $clone->width()); + $this->assertEquals(4, $result->width()); + + $this->assertEquals('ff0000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00000000', $image->pickColor(1, 0)->toHex()); + + $this->assertEquals('ff0000', $clone->pickColor(0, 0)->toHex()); + $this->assertEquals('00000000', $clone->pickColor(1, 0)->toHex()); + } + public function testDriver(): void { $this->assertInstanceOf(Driver::class, $this->image->driver()); diff --git a/tests/Drivers/Imagick/ImageTest.php b/tests/Drivers/Imagick/ImageTest.php index 57f55bf1..52247780 100644 --- a/tests/Drivers/Imagick/ImageTest.php +++ b/tests/Drivers/Imagick/ImageTest.php @@ -3,6 +3,11 @@ namespace Intervention\Image\Tests\Drivers\Imagick; use Imagick; +use ImagickPixel; +use Intervention\Image\Colors\Rgb\Channels\Alpha; +use Intervention\Image\Colors\Rgb\Channels\Blue; +use Intervention\Image\Colors\Rgb\Channels\Green; +use Intervention\Image\Colors\Rgb\Channels\Red; use Intervention\Image\Analyzers\WidthAnalyzer; use Intervention\Image\Collection; use Intervention\Image\Drivers\Imagick\Core; @@ -18,9 +23,12 @@ use Intervention\Image\Interfaces\ResolutionInterface; use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Modifiers\GreyscaleModifier; use Intervention\Image\Tests\TestCase; +use Intervention\Image\Tests\Traits\CanCreateImagickTestImage; class ImageTest extends TestCase { + use CanCreateImagickTestImage; + protected Image $image; public function setUp(): void @@ -36,6 +44,25 @@ class ImageTest extends TestCase ); } + public function testClone(): void + { + $image = $this->readTestImage('gradient.gif'); + $clone = clone $image; + + $this->assertEquals(16, $image->width()); + $this->assertEquals(16, $clone->width()); + $result = $clone->crop(4, 4); + $this->assertEquals(16, $image->width()); + $this->assertEquals(4, $clone->width()); + $this->assertEquals(4, $result->width()); + + $this->assertEquals('ff0000', $image->pickColor(0, 0)->toHex()); + $this->assertEquals('00000000', $image->pickColor(1, 0)->toHex()); + + $this->assertEquals('ff0000', $clone->pickColor(0, 0)->toHex()); + $this->assertEquals('00000000', $clone->pickColor(1, 0)->toHex()); + } + public function testDriver(): void { $this->assertInstanceOf(Driver::class, $this->image->driver()); diff --git a/tests/ImageTest.php b/tests/ImageTest.php deleted file mode 100644 index c079ec6b..00000000 --- a/tests/ImageTest.php +++ /dev/null @@ -1,53 +0,0 @@ -assertEquals(3, $image->width()); - $this->assertEquals(3, $clone->width()); - $result = $clone->resize(1); - $this->assertEquals(3, $image->width()); - $this->assertEquals(1, $clone->width()); - $this->assertEquals(1, $result->width()); - } - - public function testCloneImageImagick(): void - { - $imagick = new Imagick(); - $imagick->newImage(3, 2, new ImagickPixel('red'), 'png'); - $image = new Image( - new ImagickDriver(), - new ImagickCore($imagick) - ); - - $clone = clone $image; - - $this->assertEquals(3, $image->width()); - $this->assertEquals(3, $clone->width()); - $result = $clone->resize(1); - $this->assertEquals(3, $image->width()); - $this->assertEquals(1, $clone->width()); - $this->assertEquals(1, $result->width()); - } -}