From 0afda4b9e4752e06f2924045c40d939a0efcccc4 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 5 Nov 2021 15:48:16 +0000 Subject: [PATCH] Modifier & tests --- src/Drivers/Abstract/AbstractImage.php | 28 +++++++++ .../Gd/Decoders/BinaryImageDecoder.php | 37 +++++++++++- src/Drivers/Gd/Image.php | 14 ++++- src/Drivers/Imagick/Image.php | 10 ++++ src/Interfaces/ImageInterface.php | 3 + tests/Drivers/Gd/ImageTest.php | 56 +++++++++++++++++- .../Drivers/Gd/Modifiers/BlurModifierTest.php | 21 +++++++ .../Imagick/Modifiers/BlurModifierTest.php | 21 +++++++ tests/Traits/CanCreateGdTestImage.php | 38 ++++++++++++ tests/Traits/CanCreateImagickTestImage.php | 23 +++++++ tests/images/circle.png | Bin 0 -> 383 bytes tests/images/test.jpg | Bin 0 -> 1427 bytes tests/images/trim.png | Bin 0 -> 258 bytes 13 files changed, 245 insertions(+), 6 deletions(-) create mode 100644 tests/Drivers/Gd/Modifiers/BlurModifierTest.php create mode 100644 tests/Drivers/Imagick/Modifiers/BlurModifierTest.php create mode 100644 tests/Traits/CanCreateGdTestImage.php create mode 100644 tests/Traits/CanCreateImagickTestImage.php create mode 100644 tests/images/circle.png create mode 100644 tests/images/test.jpg create mode 100644 tests/images/trim.png diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 4c1b1fba..b315f0b1 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -36,6 +36,11 @@ abstract class AbstractImage return $this->frames; } + public function getFrame(int $key = 0): ?FrameInterface + { + return $this->frames->get($key); + } + public function addFrame(FrameInterface $frame): ImageInterface { $this->frames->push($frame); @@ -115,6 +120,16 @@ abstract class AbstractImage ); } + public function pickColors(int $x, int $y): Collection + { + $colors = new Collection(); + foreach ($this->getFrames() as $key => $frame) { + $colors->push($this->pickColor($x, $y, $key)); + } + + return $colors; + } + public function resize(...$arguments): ImageInterface { $size = $this->getResizer()->setTargetSizeByArray($arguments)->resize(); @@ -150,4 +165,17 @@ abstract class AbstractImage $this->resolveDriverClass('Modifiers\ResizeModifier', $size) ); } + + public function place($element, string $position = 'top-left', int $offset_x = 0, int $offset_y = 0): ImageInterface + { + return $this->modify( + $this->resolveDriverClass( + 'Modifiers\PlaceModifier', + $element, + $position, + $offset_x, + $offset_y + ) + ); + } } diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index 478ae7f6..32dcc6b1 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Drivers\Gd\Decoders; +use GdImage; use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\Decoders\AbstractDecoder; use Intervention\Image\Drivers\Gd\Frame; @@ -26,13 +27,15 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface return $this->decodeGif($input); // decode (animated) gif } - $resource = @imagecreatefromstring($input); + $gd = @imagecreatefromstring($input); - if ($resource === false) { + if ($gd === false) { $this->fail(); } - return new Image(new Collection([new Frame($resource)])); + $gd = $this->gdImageToTruecolor($gd); + + return new Image(new Collection([new Frame($gd)])); } protected function decodeGif($input): ImageInterface @@ -55,4 +58,32 @@ class BinaryImageDecoder extends AbstractDecoder implements DecoderInterface return $image; } + + /** + * Transform GD image into truecolor version + * + * @param GdImage $gd + * @return bool + */ + public function gdImageToTruecolor(GdImage $gd): GdImage + { + $width = imagesx($gd); + $height = imagesy($gd); + + // new canvas + $canvas = imagecreatetruecolor($width, $height); + + // fill with transparent color + imagealphablending($canvas, false); + $transparent = imagecolorallocatealpha($canvas, 255, 255, 255, 127); + imagefilledrectangle($canvas, 0, 0, $width, $height, $transparent); + imagecolortransparent($canvas, $transparent); + imagealphablending($canvas, true); + + // copy original + imagecopy($canvas, $gd, 0, 0, 0, 0, $width, $height); + imagedestroy($gd); + + return $canvas; + } } diff --git a/src/Drivers/Gd/Image.php b/src/Drivers/Gd/Image.php index be11857e..d226269a 100644 --- a/src/Drivers/Gd/Image.php +++ b/src/Drivers/Gd/Image.php @@ -8,6 +8,7 @@ use Intervention\Image\Drivers\Abstract\AbstractImage; use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Geometry\Resizer; use Intervention\Image\Geometry\Size; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\EncoderInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; @@ -17,11 +18,20 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate { public function width(): int { - return imagesx($this->frames->first()->getCore()); + return imagesx($this->getFrame()->getCore()); } public function height(): int { - return imagesy($this->frames->first()->getCore()); + return imagesy($this->getFrame()->getCore()); + } + + public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface + { + if ($frame = $this->getFrame($frame_key)) { + return new Color(imagecolorat($frame->getCore(), $x, $y)); + } + + return null; } } diff --git a/src/Drivers/Imagick/Image.php b/src/Drivers/Imagick/Image.php index 47073a25..a0c289af 100644 --- a/src/Drivers/Imagick/Image.php +++ b/src/Drivers/Imagick/Image.php @@ -7,6 +7,7 @@ use Intervention\Image\Collection; use Intervention\Image\Drivers\Abstract\AbstractImage; use Intervention\Image\Drivers\Imagick\Frame; use Intervention\Image\Geometry\Size; +use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\SizeInterface; use IteratorAggregate; @@ -22,4 +23,13 @@ class Image extends AbstractImage implements ImageInterface, IteratorAggregate { return $this->frames->first()->getCore()->getImageHeight(); } + + public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface + { + if ($frame = $this->getFrame($frame_key)) { + return new Color($frame->getCore()->getImagePixelColor($x, $y)); + } + + return null; + } } diff --git a/src/Interfaces/ImageInterface.php b/src/Interfaces/ImageInterface.php index 33231f2c..56b5ec01 100644 --- a/src/Interfaces/ImageInterface.php +++ b/src/Interfaces/ImageInterface.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Interfaces; +use Intervention\Image\Collection; use Intervention\Image\EncodedImage; interface ImageInterface @@ -14,4 +15,6 @@ interface ImageInterface public function encode(EncoderInterface $encoder): EncodedImage; public function setLoops(int $count): ImageInterface; public function loops(): int; + public function pickColor(int $x, int $y, int $frame_key = 0): ?ColorInterface; + public function pickColors(int $x, int $y): Collection; } diff --git a/tests/Drivers/Gd/ImageTest.php b/tests/Drivers/Gd/ImageTest.php index 0f966290..1635bc61 100644 --- a/tests/Drivers/Gd/ImageTest.php +++ b/tests/Drivers/Gd/ImageTest.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Tests\Drivers\Gd; use Intervention\Image\Collection; +use Intervention\Image\Drivers\Gd\Color; use Intervention\Image\Drivers\Gd\Frame; use Intervention\Image\Drivers\Gd\Image; use Intervention\Image\Geometry\Size; @@ -14,7 +15,17 @@ class ImageTest extends TestCase protected function setUp(): void { - $this->image = new Image(new Collection([new Frame(imagecreatetruecolor(3, 2))])); + $gd1 = imagecreatetruecolor(3, 2); + imagefill($gd1, 0, 0, imagecolorallocate($gd1, 255, 0, 0)); + $gd2 = imagecreatetruecolor(3, 2); + imagefill($gd2, 0, 0, imagecolorallocate($gd1, 0, 255, 0)); + $gd3 = imagecreatetruecolor(3, 2); + imagefill($gd3, 0, 0, imagecolorallocate($gd1, 0, 0, 255)); + $this->image = new Image(new Collection([ + new Frame($gd1), + new Frame($gd2), + new Frame($gd3), + ])); } public function testConstructor(): void @@ -43,4 +54,47 @@ class ImageTest extends TestCase { $this->assertInstanceOf(Size::class, $this->image->getSize()); } + + public function testPickColor(): void + { + $color = $this->image->pickColor(0, 0); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(255, $color->red()); + $this->assertEquals(0, $color->green()); + $this->assertEquals(0, $color->blue()); + + $color = $this->image->pickColor(0, 0, 1); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(0, $color->red()); + $this->assertEquals(255, $color->green()); + $this->assertEquals(0, $color->blue()); + + $color = $this->image->pickColor(0, 0, 2); + $this->assertInstanceOf(Color::class, $color); + $this->assertEquals(0, $color->red()); + $this->assertEquals(0, $color->green()); + $this->assertEquals(255, $color->blue()); + + $color = $this->image->pickColor(0, 0, 3); + $this->assertNull($color); + } + + public function testPickColors(): void + { + $colors = $this->image->pickColors(0, 0); + $this->assertInstanceOf(Collection::class, $colors); + $this->assertCount(3, $colors); + + $this->assertEquals(255, $colors->get(0)->red()); + $this->assertEquals(0, $colors->get(0)->green()); + $this->assertEquals(0, $colors->get(0)->blue()); + + $this->assertEquals(0, $colors->get(1)->red()); + $this->assertEquals(255, $colors->get(1)->green()); + $this->assertEquals(0, $colors->get(1)->blue()); + + $this->assertEquals(0, $colors->get(2)->red()); + $this->assertEquals(0, $colors->get(2)->green()); + $this->assertEquals(255, $colors->get(2)->blue()); + } } diff --git a/tests/Drivers/Gd/Modifiers/BlurModifierTest.php b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php new file mode 100644 index 00000000..9062eda4 --- /dev/null +++ b/tests/Drivers/Gd/Modifiers/BlurModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new BlurModifier(30)); + $this->assertEquals('4fa68d', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php new file mode 100644 index 00000000..a1ff3750 --- /dev/null +++ b/tests/Drivers/Imagick/Modifiers/BlurModifierTest.php @@ -0,0 +1,21 @@ +createTestImage('trim.png'); + $this->assertEquals('00aef0', $image->pickColor(14, 14)->toHex()); + $image->modify(new BlurModifier(30)); + $this->assertEquals('42acb2', $image->pickColor(14, 14)->toHex()); + } +} diff --git a/tests/Traits/CanCreateGdTestImage.php b/tests/Traits/CanCreateGdTestImage.php new file mode 100644 index 00000000..f9f4238e --- /dev/null +++ b/tests/Traits/CanCreateGdTestImage.php @@ -0,0 +1,38 @@ +testImageDecoder()->handle( + sprintf('%s/../images/%s', __DIR__, $filename) + ); + } + + public function createTestAnimation(): Image + { + $gd1 = imagecreatetruecolor(3, 2); + imagefill($gd1, 0, 0, imagecolorallocate($gd1, 255, 0, 0)); + $gd2 = imagecreatetruecolor(3, 2); + imagefill($gd2, 0, 0, imagecolorallocate($gd1, 0, 255, 0)); + $gd3 = imagecreatetruecolor(3, 2); + imagefill($gd3, 0, 0, imagecolorallocate($gd1, 0, 0, 255)); + return new Image(new Collection([ + new Frame($gd1), + new Frame($gd2), + new Frame($gd3), + ])); + } + + protected function testImageDecoder(): FilePathImageDecoder + { + return new FilePathImageDecoder(); + } +} diff --git a/tests/Traits/CanCreateImagickTestImage.php b/tests/Traits/CanCreateImagickTestImage.php new file mode 100644 index 00000000..0ff42675 --- /dev/null +++ b/tests/Traits/CanCreateImagickTestImage.php @@ -0,0 +1,23 @@ +testImageDecoder()->handle( + sprintf('%s/../images/%s', __DIR__, $filename) + ); + } + + protected function testImageDecoder(): FilePathImageDecoder + { + return new FilePathImageDecoder(); + } +} diff --git a/tests/images/circle.png b/tests/images/circle.png new file mode 100644 index 0000000000000000000000000000000000000000..0ad4e6bdafdb58ed53f52ea8b123c55a63406d07 GIT binary patch literal 383 zcmV-_0f7FAP)`e_)-1Z1?M~MNpkO3v`TLieGzzAE( zhyvF&0t86V!Zy+(S@H~p$Y49k5Pllsf(o{?3YSkpJYWa$cqdGV3U)9RLd-tc8SJ34 zxgUT8c5um@d!qFqNc-PCQ@}h?{8e_0Fi(uXl`|#GGv!ZJ>;Uu3@V#nIVV+9A-Wv_f zQw;*R290Hh`O{c13KX>zW@=rZg@R_RUOZbS-UKaa&^c)=k<+Xmwy7VGLq`@PD=1Jm;PQmwy4qtMArd zujX?bCEMQEw!i1{cGccj0qvB8_4#~O$Xba+R_tQFSQq?PSb#i;5>o~{1&3sYWcFLM z>yKK0zBOaqXrYKx?DFb3c~9)2gBe`Y{|vC*UgF>#oZs8s#D;rg-MX--=CHOpZ}L{huH!>H?tJ)SS`+g1Qh%qOLK; zHVc(O9B)=qt7fe)55&QT=nQ1jWKhs!p)odYk_Hi^MPqZR%o?fKJ1^a1xnTJU?h3k# v{YYqCbayT_YmhWm%q85Ni*@&`0%#!#dP1OW#};_{;;swaEvUkJXn*+wgY5xV literal 0 HcmV?d00001 diff --git a/tests/images/trim.png b/tests/images/trim.png new file mode 100644 index 0000000000000000000000000000000000000000..6f7bd9bbde910b6d024df22a5094b2369a270b0e GIT binary patch literal 258 zcmV+d0sa1oP)RvEwADaz}vCfAWgLl^a5>hU5Q*zRc z+T=m2n$d^67+o=@EX+@*4w02nv5~GmRvMl`7C?skC`c)2KUN?XC=M$QC>|>wC;=-0 zC=n}>P?*B>_K6`gtJDvMI7|Nc@rx0UOveQdB|iG}fZc3JPB5Mr?Ms}B?*IS*07*qo IM6N<$f^76>`Tzg` literal 0 HcmV?d00001