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());